【Java】注解

2022-10-26 14:43:05 浏览数 (2)

At the beginning, we are all children; in the end, we aspire to be angels.

Java注解

也叫元数据,用来给类,方法或属性添加一个“标记”

内置注解

代码语言:javascript复制
// 标注该方法是重写父类的方法
@Override
代码语言:javascript复制
// 标注过期弃用的方法,使用这种方法编译器会发出警告
@Deprecated
代码语言:javascript复制
// 让编译器忽略有此标记的方法或类的警告
@SuppressWarnings("all")

自定义标注

注解定义

定义注解的格式:

代码语言:javascript复制
public @interface 注解名 {
    // 注解属性
}

注解属性

也叫注解元素,类似于接口中方法的定义,只能包含一下类型,否则编译时就会报错

  1. 基本数据类型
  2. class
  3. String
  4. Annotation
  5. enum
  6. 以上数据类型的数组
代码语言:javascript复制
package Note.annotation;

enum EnumDemo {
    MAX,
    MIN
}

public @interface AnnDemo {
    int age();
    String name();
    Check check();
    EnumDemo e();
    Class c();
    
    int [] ages();
    String [] names();
    EnumDemo [] ENUMS();
    Class [] classes();
    Check [] checks();
}
元素默认值

使用 default value设置默认值

代码语言:javascript复制
public @interface AnnDemo {
    int age() default 18;
}

元注解

元注解是注解的注解,目的是方便注解的开发。

代码语言:javascript复制
// 标注注解作用的位置
@Targer
代码语言:javascript复制
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}
代码语言:javascript复制
public enum ElementType {
    TYPE, // 作用于类,接口,Enum
    FIELD,  // 域声明
    METHOD,  // 作用于方法
    PARAMETER, // 参数声明
    CONSTRUCTOR, // 构造器声明
    LOCAL_VARIABLE,  // 作用于局部变量
    ANNOTATION_TYPE,
    PACKAGE,  // 作用于包
    TYPE_PARAMETER,
    TYPE_USE
}
代码语言:javascript复制
//标注在什么级别保存该注解信息
@Retention
代码语言:javascript复制
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}

public enum RetentionPolicy {
    SOURCE, // 源码级别,将被编译器丢弃
    CLASS,  // 注解在class文件中可用,但会被VM丢弃
    RUNTIME  // 在VM中也保留,因此可以使用反射读取注解信息,一般用这个
}
代码语言:javascript复制
// 在DOC文档中保留注解
@Documented
代码语言:javascript复制
// 子类继承父类注解
@Inherited

注解的本质

写一个简单的注解,javap反编译

代码语言:javascript复制
package Note.annotation;

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Check {
}
代码语言:javascript复制
// 反编译的结果
Compiled from "Check.java"
public interface Note.annotation.Check extends java.lang.annotation.Annotation {
}

注解其实是一个继承自java.lang.annotation.Annotation的接口,

注解的使用

如果注解定义了元素,并且没给定默认值,需要在使用注解时给明确的值,格式为key = value

代码语言:javascript复制
class Demo2 {

    @AnnDemo(name="liSi", check = @Check, e=EnumDemo.MAX, c=Demo.class, ages = {12, 13}, names = {"1", "2", })
    public void func(){
        System.out.println("hello world");
    }
}

如果只定义了一个元素,并且名字叫value,使用时可以不指定key

解析注解

步骤:

  1. 获取被注解装饰的类,方法,字段的class对象
  2. 调用class.getAnnotation(Class)方法获取注解对象
代码语言:javascript复制
package Note.annotation;

@AnnDemo(name="liSi")
public class Demo2 {


    public static void main(String[] args) throws NoSuchMethodException {
        Class<Demo2> demo2Class = Demo2.class;
        AnnDemo annotation = demo2Class.getAnnotation(AnnDemo.class);
        String name = annotation.name();
        System.out.println(name);
    }
}

注解本事是一个接口,解析注解时,会在内存中生成一个实现了注解接口的子类对象,大致如:

代码语言:javascript复制
public class xxx implements AnnDemo {
    public int age() {
        return 18;
    }
    
    public String name() {
        return "lisi"
    }
}

所以我们才能调用他的像name这样的方法。

简单的测试框架

代码语言:javascript复制
package Note.annotation;

        import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Check {
}
代码语言:javascript复制
package Note.annotation;

import 上课.interfacedemo.D;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;

public class MyCheck {

    public static void main(String[] args) throws IOException {
        Demo demo = new Demo();
        int errorNum = 0;
        Class<? extends Demo> aClass = demo.getClass();
        Method[] methods = aClass.getMethods();
        FileWriter fileWriter = new FileWriter("./src/Note/annotation/error.txt", true);
        fileWriter.write("n"  aClass.getName()   "的测试结果 【"  
                new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())  " 】 n");
        fileWriter.write("======================================================================n");
        for (Method m:methods
             ) {
            if(m.isAnnotationPresent(Check.class)) {
                try {
                    m.invoke(demo);
                } catch (Exception e) {
                    errorNum   ;
                    fileWriter.write(m.getName()   " 发生异常 "   "异常类型为:"   e.getCause().getClass().getSimpleName()   "n");
                    fileWriter.write("异常原因为:"   e.getCause().getMessage()   "n");
                    fileWriter.write("----------------------------------------------------------------------------------- n");
                }
            }
        }
        fileWriter.write("======================================================================n"  
                "测试结束,共发现"   errorNum   "个异常");
        fileWriter.close();
    }
}
代码语言:javascript复制
package Note.annotation;

@SuppressWarnings("all")
public class Demo {
    @Check
    public void func1() {
        String s = null;
        s.toLowerCase();
    }

    @Check
    public void func2() {
        System.out.println("111");
    }

    @Check
    public void func3() {
        System.out.println(3/0);
    }

    @Check
    public void func4() {
        System.out.println(3   "2");
    }
}
代码语言:javascript复制
Note.annotation.Demo的测试结果 【2020-03-31 22:46:29 】 
======================================================================
func1 发生异常 异常类型为:NullPointerException
异常原因为:null
----------------------------------------------------------------------------------- 
func3 发生异常 异常类型为:ArithmeticException
异常原因为:/ by zero
----------------------------------------------------------------------------------- 
======================================================================
测试结束,共发现2个异常

0 人点赞