Java中的注解与反射

1.什么是注解?

注解(Annotation)其实就是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用Annotation,我们开发人员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。

从JDK5.0开始增加了对元数据(MetaData)的支持,也就是注解。

注解可以像修饰符一样被使用,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明,这些信息被保存到注解的"name=value"对中。

2.JDK中的元注解

JDK中的元注解用于其他注解的定义。元注解有:@Retention、@Target、@Documented、@Inherited、@Repeatable五个。

@Retention

Retention为保留维持的意思,@Retention只能用于修饰一个注解的定义,用于指定注解的生命周期,@Retention包括一个RetentionPolicy类型的成员变量,使用时必须设置值,取值有下面几个:

  • RetentionPolicy.SOURCE 在源文件中有效(即源文件保留),编译器直接丢弃这种策略的注释。
  • RetentionPolicy.CLASS 在class文件中保留(即class保留),当运行Java程序时,JVM不会保留注解。这是默认值
  • RetentionPolicy.RUNTIME 在运行时有效(即运行时保留),当运行Java程序时,JVM会保留注释。程序可以通过反射获取该注释。

@Target

Target目标的意思,@target用于修饰注解的定义,用于指定被修饰的注解能用于修饰哪些程序元素(方法、接口、字段、方法…),取值有以下几个:

  • ElementType.ANNOTATION_TYPE 可以给一个注解进行注解

  • ElementType.CONSTRUCTOR 可以给构造方法进行注解

  • ElementType.FIELD 可以给属性进行注解

  • ElementType.LOCAL_VARIABLE 可以给局部变量进行注解

  • ElementType.METHOD 可以给方法进行注解

  • ElementType.PACKAGE 可以给一个包进行注解

  • ElementType.PARAMETER 可以给一个方法内的参数进行注解

  • ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举

  • ElementType.TYPE_PARAMETER 表示该注解可以写在类型变量声明语句中(如泛型声明)

  • ElementType.TYPE_USE :表示该注解可以写在使用类型的任何语句中

后面两个去值为jdk1.8引入

@Documented

Documented为文档的意思,@Documented修饰的注解将被javadoc工具提取成文档。

注意:定义@Documented注解必须设置@Retention的值为RetentionPolicy.RUNTIME

@Inherited

Inherited为继承的意思,被@Inherited元注解修饰的注解将具有继承性,即如果某个类使用了@Inherited注解,则其子类自动具有该注解。

@Repeatable

Repeatable 是可重复的意思,它是jdk1.8引入得注解,被@Repeatable元注解修饰的注解在同一个地方可以多次使用。

举个例子,一个人他既是程序员又是产品经理,同时他还是个画家:

@interface Persons {
    Person[]  value();
}
@Repeatable(Persons.class)
@interface Person{
    String role() default "";
}
@Person(role="artist")
@Person(role="coder")
@Person(role="PM")
public class SuperMan{
}

3.JDK其他内置的注解

JDK中的元注解是为了自定义注解的内置注解,除了元注解还有另外一些内置的注解。

  • @Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
  • @Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
  • @SuppressWarnings - 指示编译器去忽略注解中声明的警告。
  • @SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
  • @FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。

4.自定义注解

自定义注解语法

元注解
public @interface 注解名 {成员变量}
  • 使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口

  • 自定义注解的成员变量在定义中以无参方法的形式来声明,其方法名和返回值定义了成员的名字和类型,类型只能是

    • 八种基本数据类型
    • String
    • Class 类
    • enum 枚举类型
    • Annotation 注解类型
    • 以上所有类型的数组
  • 可以使用default关键字指定默认值

  • 只有一个参数成员时,建议使用参数名为value

  • 如果定义的注解含有配置参数,那么使用时必须指定参数值

举例:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
@Inherited
public @interface MyAnno {
    String value() default "有注解";
}

5.注解与反射

JDK5.0在java.lang.reflect包下新增了AnnotatedElement接口,该接口代表程序中可以接受注解的程序元素,我们可以调用AnnotatedElement对象的方法来获取注解信息

 //如果存在该元素的指定类型的注解,则返回这些注解,否则返回 null。
T  getAnnotation(Class<T> annotationClass) 
 //返回此元素上存在的所有注解。        
 Annotation[]   getAnnotations() 
 //返回直接存在于此元素上的所有注解。         
 Annotation[]   getDeclaredAnnotations() 
 //如果指定类型的注解存在于此元素上,则返回 true,否则返回 false。           
 boolean    isAnnotationPresent(Class<? extends Annotation> annotationClass) 
          

举个例子:

一个类的某些字段上被注解标识,在读取该属性时,将注解中的默认值赋给这些属性,没有标记的属性不赋值

自定义注解:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
@Inherited
public @interface MyAnno {
    String value() default "有注解";
}

类:

public class Person {

    @MyAnno
    private String stra;
    private String strb;
    private String strc;

    public Person(String str1,String str2,String str3){
        super();
        this.stra = str1;
        this.strb = str2;
        this.strc = str3;
    }
// 省略set get
}

测试

public class MyTest {

    public static void main(String[] args) {
        Person person = new Person("无注解", "无注解", "无注解");
        doAnnoTest(person);
        System.out.println(person);
    }

    public static void doAnnoTest(Object obj){
        Class<?> aClass = obj.getClass();
        Field[] declaredFields = aClass.getDeclaredFields();

        for (Field field : declaredFields) {
            if(field.isAnnotationPresent(MyAnno.class)){
                MyAnno annotation = field.getAnnotation(MyAnno.class);
                if(annotation!=null){
                    String value = annotation.value();
                    Method method = null;
                    try {
                        method = aClass.getMethod("set" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1),String.class);
                        method.invoke(obj,value);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
}

输出结果:

Person(stra=有注解, strb=无注解, strc=无注解)

参考:

  • https://blog.csdn.net/KKALL1314/article/details/96481557

  • 《Java成神之路》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值