自定义注解的解释

自定义注解:
1 注解是一种元数据形式, 是属于Java的一种数据类型,和类 接口 数组 枚举类似
2 注解是用来修饰类 方法 变量 参数 包
3 注解不会对代码产生直接的影响.

注解就是一种标记:
如何自定义注解:
a:定义注解--相当于定义标记
b:配置注解--把标记打到需要用到的程序代码中
c:解析注解--在编译期或者运行期检测到标记 并进行特殊的操作

基本语法:
关键字:
@interface
底层所有自定义的注解都会自动继承java.lang.annotation.Annotation接口

根据自定义类的经验,在类的实现部分就是书写构造器 属性和方法 但是在自定义注解中 其实现部分只能定义一个东西:注解类型元素(annotation type element)
public @interface cherryAnnotation{
    public String name();
    int age() default 18;
    int[]array();
}

注解里面定义的是: 注解类型元素!!!
定义注解类型元素时注意:
1.访问的修饰符必须是public 不写是默认的public
2.该元素的类型只能是基本数据类型 String Class 枚举类型 注解类型(体现了注解的嵌套效果) 数组
3.该元素的名称一般定义为名词,如果注解中只有一个元素,请把名字起为value(后面使用会带来便利操作)
4.()不是定义方法参数的地方,也不能在括号中定义任何参数,仅仅是一个特殊的语法
5.default 代表的是默认值,值必须和第二点定义的类型一致
6.如果没有默认值,代表后续使用注解时必须给该类型元素赋值.

常用的元注解:
一个最基本的注解定义只包含了上面的两部分内容:
1:注解的名字 
2:注解包含的类型元素 但是使用jdk自带注解时候 有的注解是写在方法上 例如 @override 有的写在类上 @Deprecated
此时就是元注解:
元注解:是专门修饰注解的注解

@Target注解:
是专门用来限定某个自定义注解能够被应用在那些Java元素上面的, 它使用的一个枚举类型定义如下:
public enum ElementType{
        类 接口(包括注解类型) 枚举
        TYPE,
        属性的声明
        FIELD,
        方法的声明
        METHOD
        方法形参的声明
        PARAMETER
        构造方法的声明
        CONSTRCTOR,
        局部变量的声明
        LOCAL_VARIABLE
        注解类型的声明
        ANNOTATION
        包的声明
        PACKAGE;
}

eg:
@Target(value={ElementType.Type,ElementType.METHOD}
public @interface CherryAnnotation {
    String name();
    int age();
    int []array();
}


@Retention 注解:
翻译:持久力 保持力 即用来修饰自定义注解的生命力

注解的生命周期有三个阶段:
第一:Java源文件阶段
第二:编译到class文件阶段
第三:运行期阶段
同样使用了RetentionPolicy枚举类型定义了三个阶段:
public enum RetentionPolicy{
    注解将被编译器忽略
    SOURCE,
    注解将被编译器记录在class文件中,但在运行时不会被虚拟机保留,这是一个默认的行为
    CLASS,
    注解将被编译器记录在class文件中,并且在运行时被虚拟机保留,因此能通过反射被读取到
    RUNTIME
}

如果一个注解被定义为:
RetentionPolicy.SOURCE ,则它将被限定在Java源文件中,那个这个注解就不会参与编译,也不会在运行期间起作用,这个注解就跟注释一样,只能被阅读Java文件的人看到
RetentionPolicy.CALSS, 他将被编译到class文件中,那么编译器可以在编译时根据注解做一些处理动作,但是运行时会被虚拟机忽略的,在运行期间也不能被读取到
RetentionPolicy.RUNTIME,那么这个注解在运行期间被加载到class文件中,在运行期间我们可以通过反射得到这个注解,并通过判断是否有这个注解或者这个注解的属性值,从而执行不同的代码程序片段.
在默认情况下,自定义注解默认使用的是RententionPolicy.CLASS

@Documented注解:
是被用来指定自定义注解是否能随着被定义的Java文件生成到JavaDoc文档中

@Inherited注解:
是指定某个自定义注解如果写在了父类的声明部分,那么子类的声明部分也能自动拥有该注解.@Inherited注解只对那些@Target被定义为ElementType.Type的自定义注解起作用.


在具体的Java类上使用注解:
首先定义一个注解,和一个供注解修饰的简单Java类:
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CherryAnnotation{
    String name();
    int age() default 18;
    int [] score;
}
public class Student{
    public void study(int times){
    fore(int i=0;i     System.out.println("aa");
    }

}

分析:
1.CherryAnnotation的@Target的定义为ElementType.METHOD,那么它书写的位置应该在方法的定义的上方,即在study()上面
2.由于我们在CherryAnnotation中定义的有注解类型,而且有些元素没有默认值,这就要求我们在使用的时候必须在标记后面打上(),并且在()内以"元素名 = 元素值" 的形式挨个填上所有没有默认值的注解类型元素(有默认值的也可以填上重新赋值),中间用","号分割.
最终的书写:
public class Student{
@CherryAnnotation(name = "cherry-peng",age=23,score = {11,22,33}
public void study(int times){
    fore(int i=0;i     System.out.println("aa");
    }
}

特殊语法:
第一种:
如果注解本身没有注解类型元素,那么使用注解的时候可以省略(),直接写为:@注解名,它和标准的语法@注解名()等效
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.Type})
@Documented
public @interface FirstAnnotation{

}

//等效于@FirstAnnotation
@FirstAnnotation
public class JavaBean{
}

第二种:
如果注解本身只有一个注解类型元素,而且命名为value,那么在使用注解的时候可以直接使用:@注解名(注解值),等效于@注解名(value=注解值)
@Retention(RetentionPolicy.RUNTIME)
@Target(value={ElementType.TYPE})
@Documented
public @interface SecondAnnotation{
    String value();
}

//等效于@SecondAnnotation(value="this is second annotation")
@SecondAnnotation("this is annotation")
public class JavaBean{
}

第三种:
如果注解中的某个注解类型元素时一个数组类型,在使用时又出现只需要一个值的情形,那么在使用注解时可以直接写:@注解名(类型名 = 类型值),它和标准写法:@注解名(类型名 = {类型值})等效
@Retention(RetentionPolicy.RUNTIME)
@Target(value={ElementType.TYPE})
@Documented
public @interface SecondAnnotation{
    String []name();
}

@ThirdAnnotation(name ="this is third annotation")
public class JavaBean{
}

第四种:
如果一个注解的@Target是定义为Element.PACKAGE,那么这个注解是配置在package-info.java中的,而不能直接在某个类的package代码上面配置


自定义注解运行时解析:

@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CherryAnnotation{
    String name();
    int age() default 18;
    int [] score;
}
public class Student{
@CherryAnnotation(name = "cherry-peng",age=23,score = {11,22,33}
public void study(int times){
    fore(int i=0;i     System.out.println("aa");
    }
}

注解保持力的三个阶段:
1.Java源文件阶段
2.编译到class文件阶段
3.运行期阶段
只有当注解的保持力处于运行阶段,即使用@Retention(RetentionPolicy.RUNTIME)修饰注解十,才能在jvm运行时,检测到注解,并运行一系列操作.


反射操作获取注解:
在运行期探究和使用编译期的内容,(编译器配置的注解) 要使用反射.
public class TestAnnotation {
    public static void main(String[] args){
        try {
            //获取Student的Class对象
            Class stuClass = Class.forName("pojos.Student");

            //说明一下,这里形参不能写成Integer.class,应写为int.class
            Method stuMethod = stuClass.getMethod("study",int.class);

            if(stuMethod.isAnnotationPresent(CherryAnnotation.class)){
                System.out.println("Student类上配置了CherryAnnotation注解!");
                //获取该元素上指定类型的注解
                CherryAnnotation cherryAnnotation = stuMethod.getAnnotation(CherryAnnotation.class);
                System.out.println("name: " + cherryAnnotation.name() + ", age: " + cherryAnnotation.age()
                    + ", score: " + cherryAnnotation.score()[0]);
            }else{
                System.out.println("Student类上没有配置CherryAnnotation注解!");
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

分析:
1.如果我们要获得的注解是配置在方法上的,那么我们要从Method对象上获取,如果是配置在属性上,就需要从属性对应的Field对象上获取,如果是配置对应的Field对象上去获取,如果是配置在类型上,需要从Class对象上获取
2.isAnnotationPresent(Class annotationClass) 方法是专门判断该元素上是否配置某个指定的注解.
3.getAnnotation(Class annotationClass)方法时获取该元素上指定的注解,之后再调用该注解的注解类型元素方法就可以配置使的值数据.
4.反射对象上还有一个方法:
getAnnotation(),该方法获得该对象身上配置的所有注解.它会返回给我们一个注解数组,需要注意的该数组的类型是Annotation类型,这个Annotation是一个来自于java.lang.annotation包的接口.

你可能感兴趣的:(Java)