Java注解

Java注解 (Annotation)


A.使用注解

1.什么是注解

  • 注解是放在Java源代码的类、方法、字段、参数前的一种标签

2.注解的作用

  • 注解本身对代码逻辑没有任何影响
  • 如何使用注解有工具(编译器)决定
  • 编译器可以使用注解

3.定义配置参数

  • 配置参数由注解类型定义

  • 配置参数可以包括:

    • 所有基本类型
    • String
    • 枚举类型
    • 数组
  • 配置参数必须是常量

  • 缺少某个配置参数将使用默认值

  • 如果只写常量,相当于省略了value参数。(如果参数名称是value,可以省略参数名称)

  • 如果只写注解,相当于全部使用默认值

public class A{
    @Check(min=0,max=100,value=20)
    private int m;
    @Check(value=20)
    private int n;
    @Check(20) // 相当于 @Check(value=20)
    private int x;
    @Check
    private int y;
}

B.定义注解

1.使用@interface定义注解

  • 注解的参数类似无参数方法
  • 可以设定一个默认值(推荐)
  • 把最常用的参数命名为value(推荐)
public @interface Report{
    int type() default 0;
    String level() default "info";
    String value() default "";
}

2.元注解

2.1使用@Target定义注解可以被应用于源码的位置:

  • 类或接口: ElementType.TYPE
  • 字段: ElementType.FIELD
  • 方法: ElementType.METHOD
  • 构造方法: ElementType.CONSTRUCTOR
  • 方法参数: ElementType.PARAMETER
//只能用于方法的@Report注解
@Target( ElementType.METHOD)
public @interface Report{
    int type() default 0;
    String level() default "info";
    String value() default "";
}

//用于方法和字段的@Report注解
@Target({ 
    ElementType.METHOD,
    ElementType.FIELD 
})
public @interface Report{
    int type() default 0;
    String level() default "info";
    String value() default "";
}

2.2使用@Retention定义注解的生命周期

  • 仅编译期:RetentionPolicy.SOURCE
  • 仅class文件:RetentionPolicy.CLASS
  • 运行期:RetentionPolicy:RUNTIME

注意:@Retention不存在,则该注解默认为CLASS。通常自定义的注解都是RUNTIME

  • 注解的生命周期
    • RetetionPolicy.SOURCE:编译器在编译时直接丢弃,如@Override
    • RetetionPolicy.CLASS:该注解仅存储在class文件中
    • RetentionPolicy.RUNTIME:在运行期可以读取该注解

2.3使用@Repeatable定义注解是否可重复

  • 条件是JDK版本>=1.8
@Repeatable
@Target( ElementType.TYPE)
public @interface Report{
    int type() default 0;
    String level() default "info";
    String value() default "";
}

@Report(type=1,level="debug")
@Report(type=2,level="warning")
public class hello(){
    
}

2.4使用@Inherited定义子类是否可继承父类定义的注解

  • 仅针对@Target为TYPE类型的注解
  • 仅针对class的继承
  • 对interface的继承无效
@Inherited
@Target( ElementType.TYPE)
public @interface Report{
    int type() default 0;
    String level() default "info";
    String value() default "";
}

@Report(type=1)
public class Person(){
    
}

public class Student extends Person(){
    
}

2.5小结,定义注解的步骤

  1. 用@interface定义注解

  2. 用元注解(meta annotation)配置注解

    • Target : 必须设置
    • Retention : 一般设置为RUNTIME
    • 通常不必写@Inherited,@Repeatable等等
  3. 定义注解参数和默认值

@Target( ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Report{
    int type() default 0;
    String level() default "info";
    String value() default "";
}

C.处理注解

1.如何读取RUNTIME类型的注解?方法是是用:反射

  • 注解也是class
  • 所有的注解继承自java.lang.annotation.Annotation
  • 使用反射API

2.使用反射API读取注解,判断注解是否存在

  • Class.isAnnotationPresent(Class) //用isAnnotationPresent(Class)判断一个注解是否存在
  • Field.isAnnotationPresent(Class)
  • Method.isAnnotationPresent(Class)
  • Constructor.isAnnotationPresent(Class)
Clss cls = Person.class;
//判断@Report是否存在
cls.isAnnotationPresent(Report.class);

3.使用getAnnotation(Class)来获得一个注解

//使用getAnnotation(Class)来获得一个注解,如果不存在则返回null
Report report = cls.getAnnotation("Report.class");
int type = report.type();
String level = report.level();

4.使用isAnnotationPresent(Class)来判断一个注解是否存在

//判断一个annotation是否存在
Clss cls = Person.class;
if(cls.isAnnotationPresent(Report.class)){
    Report report = cls.getAnnotation(Report.class);
    if(report != null){
        ...
    }
}

5.读取方法参数的注解

public String hello(@NotNull @Range(max=6) String name, @NotNull String prefix){
    ...
}

//由于读取方法参数的注解,
//方法参数名作为一个数组,而每个方法参数又可以定义多个注解,
//所以一次获得方法的所有注解,就有必须用二维数据来接收.

/*
annos 的值为:
{
 {@NotNull, @Range(max=5)},
 {@NotNull}
}
*/
Method m = ...
Annotation[][] annos = m.getParameterAnnotation();
Annotation[] annosOfName = annos[0]; //name参数的Annotation
for(Annotation anno : annosOfName){
    if(anno instanceof Range){
        Range r = (Range) anno;
        ...
    }
}

6.使用注解的综合实例

  • 定义两个注解
//NotNull.java
@Retention(RetentionPolicy.RUNTIME) //不要漏写,否则运行时不起作用
@Target(ElementType.FIELD)
public @interface NotNull{
    
}

//Range.java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Range{
    int min() default 1;
    int max() default 100;
}
  • 定义一个测试类Person
//Person.java
public class Person{
    @NotNull
    private String name;
    
    @Range(max = 22)
    private int age;
    
    public Person(String name ,int age){
        this.name = name;
        this.age = age;
    }
    
    public static void main(String args[]) throws Exception{
        Person p1 = new Person("xiao ming",25);
        Person p2 = new Person(null,10);
        checkPerson(p1);
        checkPerson(p2);
    }
    
    static void checkPerson(Person p) throws Exception{
         System.out.prinln("check" + p + "...");
        Class cls = Person.class;
        for (Field f : cls.getFields()){
            checkField(f, p);
        }
    }
    
    static void checkField(Field f, Person p) throws Exception{
        if(f.isAnnotationPresent(NotNull.class)){
            Object o = f.get(p);
            if(o == null){
                System.out.prinln("Error:field"+f.getName+"is null");
            }
        }        
        if(f.isAnnotationPresent(Range.class)){
            Range range = f.getAnnotation(Range.class);
            int n = (Integer) f.get(p);
            if(n<range.min() || n > range.max()) {
                System.out.prinln("Error:field"+f.getName+"is out of range.");
            }
        }
    }
}

the end.

你可能感兴趣的:(Java基础,Java注解)