美文网首页
Annotation基础学习上

Annotation基础学习上

作者: jack_do | 来源:发表于2019-03-27 15:14 被阅读0次

    1.Annotation基本信息
    1.1:背景
    从jdk 5 开始,java增加了对元数据(MetaData)的支持,也就是Annotation(注解),元数据就是Annotation
    1.2:概念:
    Annotation是一个接口,程序可以通过反射来获取指定程序元素的Annotation对象,然后通过Annotation对象来取得注解里的元数据。
    1.3:作用:
    为程序元素(类、方法、成员变量等)设置元数据后,开发人员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息,这些元数据可以在编译、类加载、运行时被读取,并执行相应的处理。可以做额外的功能,可以分析代码质量等。
    1.4:形式:
    Annotation以"name = value"形式显示,如果

    2.Annotation分类
    2.1基本Annotation

    @Override 限定重写父类的方法
    @Deprecated 标示已过时
    @SuppressWarnings 抑制编译器警告
    @FunctionalInterface 用于编译级错误检查,提醒编译器去检查该接口是否 仅包含一个抽象方法

    //压制警告
    @SuppressWarnings("unchecked")
    public class People {
    
        public static void main(String[] args){
            Set set = new TreeSet();
            set.add("a");
            /**
             * 将一个无泛型的Set传递给了varagMethod方法,此时就有可能造成堆污染
             * 把一个不带泛型的对象赋给一个带泛型的变量时候,往往会发生这种"堆污染(Heap Pollution)"情况
             */
            eat(set);
        }
    
        public static void eat(Set<Integer> objects) {
            /**
             * Exception in thread "main" java.lang.ClassCastException:
             * java.base/java.lang.String cannot be cast to java.base/java.lang.Integer
             */
            objects.add(10);// ClassCastException thrown
        }
    
        /**
         * 吃的方法
         */
        public void eat(){
            System.out.println("people eat method");
        }
    
        public void eat(List<String> fruit){
            //
            List list = fruit;
        }
    
        /**
         * 狗类
         */
        class man extends People {
            /**
             * 规定狗吃的方法继承自动物,就加上该@Override注解
             */
            @Override
            public void eat(){
                System.out.println("man eat method");
            }
    
            /**
             * 定义标识该方法已过期,以后不建议使用该方法
             */
            @Deprecated
            public  void go(){
    
            }
        }
    
        /**
         * drinkwater接口只允许有一个抽象方法
         */
        @FunctionalInterface
        interface drinkwater{
            void warter();
        }
    
    }
    
    

    2.2jdk的元Annotaion

    1:@Retention 保留; 记忆力
    @Retention只能用于修饰一个Annotation定义,用于指定被修饰的Annotation可以保留多长时间
    @Retention包含了一个RetentionPolicy类型的value成员变量,所以使用@Retention时必须为该
    value成员变量指定值
    value成员变量的值只能是以下三个
    a: RetentionPolicy.CLASS
    @Retention(value = RetentionPolicy.CLASS)
    @Retention(RetentionPolicy.CLASS)
    编译器把Annotation记录在class文件中,当运行java程序时,jvm不再保留Annotation
    b: RetentionPolicy.RUNTIME
    @Retention(value = RetentionPolicy.RUNTIME)
    @Retention(RetentionPolicy.RUNTIME)
    编译器把Annotation记录在class文件中,当运行java程序时,jvm会保留Annotation,程序可以
    通过反射获取该Annotation信息
    c:RetentionPolicy.SOURCE
    @Retention(value = RetentionPolicy.SOURCE)
    @Retention(RetentionPolicy.SOURCE)
    Annotation只保留在源代码中,编译器直接丢弃这种Annotation
    2:@Target (服务的)对象; 目标
    @Target只能用于修饰一个Annotation定义,它用于指定被修饰的Annotation能用于修饰那些程序单元
    a:ElementType.ANNOTATION_TYPE
    指定该策略的Annotation只能修饰Annotation
    b:ElementType.CONSTRUCTOR
    指定该策略的Annotation只能修饰构造器
    c:ElementType.FIELD
    指定该策略的Annotation只能修饰成员变量
    d:ElementType.LOCAL_VARIABLE
    指定该策略的Annotation只能修饰局部变量
    e:ElementType.METHOD
    指定该策略的Annotation只能修饰方法定义
    f:ElementType.PACKAGE
    指定该策略的Annotation只能修饰包定义
    g:ElementType.PARAMETER
    指定该策略的Annotation只能修饰参数
    h:ElementType.TYPE
    指定该策略的Annotation只能修饰 类 接口 枚举
    3:@Documented
    注解表明这个注解应该被 javadoc工具记录. 默认情况下,javadoc是不包括注解的.
    但如果声明注解时指定了 @Documented,则它会被 javadoc 之类的工具处理,
    所以注解类型信息也会被包括在生成的文档中
    4:@Inherited
    这是一个稍微复杂的注解类型. 它指明被注解的类会自动继承. 更具体地说,
    如果定义注解时使用了 @Inherited 标记,然后用定义的注解来标注另一个父类,
    父类又有一个子类(subclass),则父类的所有属性将被继承到它的子类中.

    2.3自定义Annotation

    定义新的Annotation类型使用@interface关键字,它用于定义新的Annotation类型。定义一个新的 Annotation类型与定义一个接口非常像,Annotation前面比接口前面多一个@,如下代码可定义一个简单的Annotation:

    public @interface Login {
    }
    

    定义了该Annotation之后,就可以在程序任何地方来使用该Annotation,使用Annotation时的语法非常类似于public、final这样的修饰符。通常可用于修饰程序中的类、方法、变量、接口等定义,通常我们会把Annotation放在所有修饰符之前,而且由于使用Annotation时可能还需要为其成员变量指定值,因而Annotation长度可能比较长,所以通常把Annotation另放一行,如下程序所示:

    /**
    * 定义一个Annotation
    */
    /**
    * 定义一个Annotation
    */
    public @interface Login {
     
    }
    
    class LoginTest{
       /**
        * 使用Annotation
        */
       @Login  
       public void login(){
           
       }
    }
    

    Annotation不仅可以是这种简单Annotation,Annotation还可以带成员变量,Annotation的成员变量在Annotation定义中以无参数方法的形式声明。其方法名和返回值定义了该成员的名字和类型。如下代码可以定义一个有成员变量的Annotation:

    /**
     * 定义一个注解
     */
    public @interface Login {
        //定义两个成员变量
        String username();
        String password();
    }
    

    一旦在Annotation里定义了成员变量之后,使用该Annotation时应该为该Annotation的成员变量指定值,如下代码所示:

    /**
     * 定义一个注解
     */
    public @interface Login {
        //定义两个成员变量
        String username();
        String password();
    }
    
    class LoginTest{
        /**
         * 使用注解
         */
        @Login(username="jack", password="123456")
        public void login(){
            
        }
    }
    

    我们还可以在定义Annotation的成员变量时为其指定初始值,指定成员变量的初始值可使用default关键字,如下代码:

    /**
     * 定义一个注解
     */
    public @interface Login {
        //定义两个成员变量
        //以default为两个成员变量指定初始值
        String username() default "jack";
        String password() default "123456";
    }
    

    如果为Annotation的成员变量指定了默认值,使用该Annotation则可以不为这些成员变量指定值,而是直接使用默认值。如下代码:

    /**
     * 定义一个注解
     */
    public @interface Login {
        //定义两个成员变量
        //以default为两个成员变量指定初始值
        String username() default "jack";
        String password() default "123456";
    }
    
    class LoginTest{
        /**
         * 使用注解
         * 因为它的成员变量有默认值,所以可以无须为成员变量指定值,而直接使用默认值
         */
        @Login
        public void login(){
            
        }
    }
    

    2.3提取Annotation的信息
    当开发者使用Annotation修饰了类、方法、Field等成员之后,这些Annotation不会自己生效,必须由开发者提供相应的工具来获取并处理Annotation信息。
    java使用Annotation接口来代表程序元素前面的注释,该接口是所有Annotation类型的父接口.
    Java 5 在java.lang.reflect包下新增了AnnotateElement接口,该接口代表程序中可以接受注释的程序元素,该接口主要有如下几个实现类(注意以下是类):

    a: Class:类定义。
    b: Constructor:构造器定义。
    c: Field:类的成员变量定义。
    d: Method:类的方法定义。
    e: Package:类的包定义。

    java.lang.reflect包下主要包含一些实现反射功能工具类,实际上,java.lang.reflect包提供的反射API扩充了读取运行时Annotation的能力。当一个Annotation类型被定义为运行时Annotation后,该注解才是运行时可见,jvm才会在装载*.class文件时读取保存在class文件中的Annotation。

    AnnotatedElement接口是所有程序元素(如Class、Method、Constructor)的父接口,所以程序通过反射获取了某个类的AnnotatedElement对象(如Class、Method、Constructor)之后,程序就可以调用该对象的如下三个方法来访问Annotation信息:

    getAnnotation(Class<T> annotationClass);
    //返回该程序元素上存在的、指定类型的注释,如果该类型的注释不存在,则返回null。

    Annotation[] getAnnotations();
    //返回该程序元素上存在的所有注释。

    boolean isAnnotationPresent(Class<? extends Annotation> annotationClass);
    //判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false。

    import java.lang.annotation.*;
    import java.lang.reflect.Method;
    
    //@Retention注解指定Login注解可以保留多久
    @Retention(RetentionPolicy.RUNTIME)
    //@Target注解指定注解能修饰的目标(只能是方法)
    @Target(ElementType.METHOD)
    
    @interface Login{
        String userName() default "jack";
        String passWord() default "123456";
    }
    
    public class Briup {
    
        public static void main(String[] args){
    
            try {
                //通过反射获取info方法类
                Method method = Briup.class.getMethod("study");
                //判断该方法上是否存在@Login注释
                boolean isAnnotationPresent = method.isAnnotationPresent(Login.class);
                if(isAnnotationPresent){
                    System.out.println("info方法上存在@Login注释");
                }else {
                    System.out.println("info方法上不存在@Login注释");
                }
    
    
                /**
                 * 获取方法上的所有注释
                 */
                Annotation[] annotations = method.getAnnotations();
                for(Annotation a : annotations){
    
                    //如果是@Login注释,则强制转化,并调用username方法,和password方法。
                    if(a!=null && a instanceof Login){
    
                        String userName = ((Login) a).userName();
                        String passWord = ((Login) a).passWord();
                        System.out.println("userName: "+userName);
                        System.out.println("passWord: "+passWord);
                    }
                    System.out.println(a);
    
                }
    
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
    
        }
    
        @Login
        @Deprecated
        public void study(){
    
        }
    }
    

    下面分别介绍两个使用Annotation的例子,第一个Annotation @Test没有任何成员变量,仅是一个标记Annotation,它的作用是标记哪些方法是可测试的。

    import java.lang.annotation.*;
    import java.lang.reflect.Method;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    @interface Test{
    
    }
    
    class Junit {
    
        @Test
        public static void test1(){
    
        }
    
        public static void test2(){
    
        }
    
        public static void test3(){
    
        }
    
        @Test
        public static void test4(){
    
        }
    }
    
    public class Briup {
    
        public static void main(String[] args){
    
            //1.1通过反射获取类
            try {
                Class<?> forName = Class.forName("demo_annotation.Junit");
                //1.2获取该类自身声明的所有方法
                Method[] methods = forName.getDeclaredMethods();
                int checkCount = 0; //测试的数量
                int uncheckCount = 0;  //未测试的数量
                for (Method method : methods) {
                    if(method.isAnnotationPresent(Test.class)){
                        checkCount++;
                    }else{
                        uncheckCount++;
                    }
                }
                System.out.println("测试的方法有" + checkCount);
                System.out.println("未测试的方法有" + uncheckCount);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    

    输出结果:
    测试的方法有2
    未测试的方法有2

    相关文章

      网友评论

          本文标题:Annotation基础学习上

          本文链接:https://www.haomeiwen.com/subject/egvzvqtx.html