1. Annotation定义规范

  注解类似一个标签,作为元数据来描述Java代码。

  注解通过 @interface 关键字进行定义。注解的定义类似接口定义,只是比接口定义多了个@。

public @interface TestAnnotation{
}

@TestAnnotation
public class Test {
}

  不过想要注解正常工作,还需要理解下元注解

 

2. 元注解

   元注解是最基本的注解,用于描述和解释其他注解。元注解有@Retention、@Documented、@Target、@Inherited、@Repeatable

2.1 @Retention注解

  Retention是保留期的意思,表示该注解的存活有效期

  RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。

  RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。

  RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。

2.2 @Target注解

  目标,用于标注该注解用于什么地方。比如只能用于方法、属性、方法参数等。

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

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

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

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

2.3 @Inherited注解

  继承的含义,比如注解 Test 被 @Inherited 修饰,之后类 A 被 Test 注解,类 B 继承 A,类 B 也拥有 Test 这个注解。可以理解为父亲有钱,那么儿子也是富豪。

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface Test {}


@Test
public class A {}


public class B extends A {}  // 类B默认也拥有Test注解

2.4 @Repeatable注解

  可重复的,表示注解的值可以取多个。比如,一个人他既是程序员又是产品经理,同时他还是个画家。

@interface Persons {    
    Person[]  value();  // 属性类型是一个被 @Repeatable 注解过的注解数组
}


@Repeatable(Persons.class)   // 括号中的相当于容器注解,用来存放其他注解
@interface Person{
    String role() default "";
}


@Person(role="artist")
@Person(role="coder")
@Person(role="PM")
public class SuperMan{
    
}

  通俗来说,Persons 是一张总的标签,上面贴满了 Person 这种同类型但内容不一样的标签。把 Persons 给一个 SuperMan 贴上,相当于同时给他贴了程序员、产品经理、画家的标签。

2.5 @Documented注解

  注解包含的元素可以被包含至Javadoc中

 

3 注解的属性

  注解的属性也就是成员变量。并且注解只有成员变量,没有方法。定义注解的属性以“无参的方法”形式声明。方法名字表明了属性的名称,返回值表明了成员变量的类型。同时注意:类型必须是 8 种基本数据类型外加 类、接口、注解及它们的数组。注解中属性可以有默认值,默认值需要用 default 关键值指定。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {  
    int id();   
    String msg() default "Hi";  // 带默认值的属性
}

 

4 注解的提取和使用

  注解的提取需要使用反射。

@TestAnnotation(msg="hello")
public class Test {
    
    @Check(value="hi")
    int a;
    
    @Perform
    public void testMethod(){}
    
    @SuppressWarnings("deprecation")
    public void test1(){
        Hero hero = new Hero();
        hero.say();
        hero.speak();
    }


    public static void main(String[] args) {
        
     // 使用Class对象的 isAnnotationPresent方法判断是否有某个注解 
        boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
        
        if ( hasAnnotation ) {
       // 获取 Annotation对象
            TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
            // 获取类的注解
            System.out.println("id:"+testAnnotation.id());
            System.out.println("msg:"+testAnnotation.msg());
        }
        
        
        try {
            Field a = Test.class.getDeclaredField("a");
            a.setAccessible(true);
            // 获取一个成员变量上的注解
            Check check = a.getAnnotation(Check.class);
            
            if ( check != null ) {
                System.out.println("check value:"+check.value());
            }
            
            Method testMethod = Test.class.getDeclaredMethod("testMethod");
            
            if ( testMethod != null ) {
                // 获取方法中的所有注解
                Annotation[] ans = testMethod.getAnnotations();
                for( int i = 0;i < ans.length;i++) {
                    System.out.println("method testMethod annotation:"+ans[i].annotationType().getSimpleName());
                }
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
            System.out.println(e.getMessage());
        } catch (SecurityException e) {
            e.printStackTrace();
            System.out.println(e.getMessage());
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
            System.out.println(e.getMessage());
        }    

    }
}

 

5 总结

  1. 注解的创建如同创建了个接口,但是多了个@

  2. 用于定义注解的元注解(合适失效、应用于什么地方、是否可继承和重复)

  3. 注解的属性(定义的规范格式、属性的类型值)

  4. 注解的提取需要使用反射,反射比较慢,因此使用是也需要注意。