美文网首页
java注解学习

java注解学习

作者: weiinter105 | 来源:发表于2019-02-22 17:44 被阅读0次

    前言

    注解(Annotation)很重要,未来的开发模式都是基于注解的,JPA是基于注解的,Spring2.5以上都是基于注解的,Hibernate3.x以后也是基于注解的,现在的Struts2有一部分也是基于注解的了,注解是一种趋势,现在已经有不少的人开始用注解了,注解是JDK1.5之后才有的新特性
    JDK1.5之后内部提供的三个注解
    @Deprecated 意思是“废弃的,过时的”
    @Override 意思是“重写、覆盖”
    @SuppressWarnings 意思是“压缩警告”

    注解的定义

    Java 官方文档对应注解的描述:注解是一系列元数据,它提供数据用来解释程序代码,但是注解并非是所解释的代码本身的一部分。注解对于代码的运行效果没有直接影响。

    注解与元与注解

    元注解就是注解的注解;用于对注解的使用条件进行限定
    @Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
    @Documented - 标记这些注解是否包含在用户文档中。
    @Target - 标记这个注解应该是哪种 Java 元素。
    @Inherited -如果一个父类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了父类的注解

    主要是@Retention和@Target
    @Retention 标记注解的的存活时间。取值如下:
    RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。一般这类Annotation用作IDE或者Compiler的检查,比如@Override。
    RetentionPolicy.CLASS 默认值,注解只被保留到编译进行的时候,在class文件中可用,它并不会被加载到 JVM 中。
    RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以通过反射获取到它们。
    注:从这里看,注解可以做很多事情,编译时检查错误,自动生成代码;运行时通过反射赋值,注入操作。
    @Target:标记这个注解可以应用到哪种 Java 元素。取值如下:
    ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
    ElementType.CONSTRUCTOR 可以给构造方法进行注解
    ElementType.FIELD 可以给属性进行注解
    ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
    ElementType.METHOD 可以给方法进行注解
    ElementType.PACKAGE 可以给一个包进行注解
    ElementType.PARAMETER 可以给一个方法内的参数进行注解
    ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举
    ElementType.TYPE_PARAMETER,标明注解可以用于类型参数声明(1.8新加入)
    ElementType.TYPE_USE 类型使用声明(1.8新加入)

    注解的属性

    注解的属性也叫做成员变量。注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型

    @Retention(RetentionPolicy.RUNTIME) //元注解
    @Target({ElementType.METHOD,ElementType.TYPE}) //元注解
    //新建一个Annotation 注解类
    public @interface MyAnnotation {
       //可以为注解类添加属性
       /*注解的属性也叫做成员变量。注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,
        * 其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型
        * */
        String name() default "weijuncheng";
        String id() default "1";
        String test();
        /*
         * 在注解中定义属性时它的类型必须是 8 种基本数据类型(int,float,boolean,byte,double,char,long,short)
         * 和(String,Class,enum,Annotation,各类型的数组
         * 注意添加的类型必须是注解类型
         * */
        
    }
    

    @Option注解中定义了多个属性。在注解中定义属性时它的类型必须是 8 种基本数据类型(int,float,boolean,byte,double,char,long,short)和(String,Class,enum,Annotation,各类型的数组)。注解中属性可以有默认值,默认值需要用 default 关键值指定。在使用的时候,我们应该给它们进行赋值

    获取注解的相关方法

    // 元素上是否存在指定类型的注解
                public boolean isAnnotationPresent(Class annotationClass) {}
     
             // 元素上如果存在指定类型的注解,则返回Annotation 对象,否则返回 null。
                public  A getAnnotation(Class annotationClass) {}
     
             // 返回此元素上存在的所有注解,包括从父类继承的
             public Annotation[] getAnnotations() {}
     
             // 返回直接存在于此元素上的所有注解,
             // 注意,不包括父类的注解,调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响,没有则返回长度为0的数组。
             public Annotation[] getDeclaredAnnotations() {} 
    

    常见注解

    @Documented
    @Retention(value=RUNTIME)
    public @interface Deprecated
    
    @Target(value=METHOD)
    @Retention(value=SOURCE)
    public @interface Override
    
    @Target(value={TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})
    @Retention(value=SOURCE)
    public @interface SuppressWarnings
    

    示例

    import java.lang.annotation.*;
    
    @Retention(RetentionPolicy.RUNTIME) //元注解
    /*
    RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。一般这类Annotation用作IDE或者Compiler的检查,比如@Override。
    RetentionPolicy.CLASS 默认值,注解只被保留到编译进行的时候,在class文件中可用,它并不会被加载到 JVM 中。
    RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以通过反射获取到它们。
     * */
    
    @Target({ElementType.METHOD,ElementType.TYPE}) //元注解
    /*
     * 
    ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
    ElementType.CONSTRUCTOR 可以给构造方法进行注解
    ElementType.FIELD 可以给属性进行注解
    ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
    ElementType.METHOD 可以给方法进行注解
    ElementType.PACKAGE 可以给一个包进行注解
    ElementType.PARAMETER 可以给一个方法内的参数进行注解
    ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举
    ElementType.TYPE_PARAMETER,标明注解可以用于类型参数声明(1.8新加入)
    ElementType.TYPE_USE 类型使用声明(1.8新加入)
     * */
    //新建一个Annotation 注解类
    public @interface MyAnnotation {
       //可以为注解类添加属性
       /*注解的属性也叫做成员变量。注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,
        * 其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型
        * */
        String name() default "weijuncheng";
        String id() default "1";
        String test();
        /*
         * 在注解中定义属性时它的类型必须是 8 种基本数据类型(int,float,boolean,byte,double,char,long,short)
         * 和(String,Class,enum,Annotation,各类型的数组
         * 注意添加的类型必须是注解类型
         * */
        
    }
    
    import java.lang.annotation.*;
    
    @Retention(RetentionPolicy.RUNTIME) 
    @Target({ElementType.METHOD}) 
    public @interface SimulateHiddenAPI {
        
    }
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Retention(RetentionPolicy.RUNTIME) 
    @Target({ElementType.FIELD}) 
    public @interface FieldAnnotation {
       String s1() default "s1";
       String s2() default "s2";
    }
    
    import java.lang.reflect.Field;
    
    public class SetFieldofTestAnnotation {
    
        public static void changeFieldofTestAnnotation() {
            try {
                Field f1 = TestAnnotation.class.getDeclaredField("s1");
                /*
                   getDeclaredField是可以获取一个类的所有字段. 
                   getField只能获取类的public 字段. 
                 * */
                if(f1.isAnnotationPresent(FieldAnnotation.class)) {
                    f1.setAccessible(true);
                    try {
                        f1.set(String.class, f1.getAnnotation(FieldAnnotation.class).s1());
                    } catch (IllegalArgumentException | IllegalAccessException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            } catch (NoSuchFieldException | SecurityException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            
            try {
                Field f2 = TestAnnotation.class.getDeclaredField("s2");
                if(f2.isAnnotationPresent(FieldAnnotation.class)) {
                    f2.setAccessible(true);  
                    try {
                        f2.set(String.class, f2.getAnnotation(FieldAnnotation.class).s2());
                    } catch (IllegalArgumentException | IllegalAccessException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            } catch (NoSuchFieldException | SecurityException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    
        }
    
    }
    
    import java.lang.reflect.Field;
    
    @MyAnnotation(id="2", test = "") //自定义注解
    //给了id的值为2,没给就是默认的值
    //即没在这里赋值,又没给默认值就会报错
    public class TestAnnotation {
        
        @FieldAnnotation
        private static String s1 = "o1";
        
        @FieldAnnotation
        private static String s2 = "o2";
        
        @SimulateHiddenAPI
        public static void test1() {
            try {
                if(TestAnnotation.class.getDeclaredMethod("test1").isAnnotationPresent(SimulateHiddenAPI.class)) {
                    System.out.println("test1 禁止调用");
                }
                else {
                    System.out.println("test1 正常调用");
                }
            } catch (NoSuchMethodException | SecurityException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
        public static void test2() {
            try {
                if(TestAnnotation.class.getDeclaredMethod("test2").isAnnotationPresent(SimulateHiddenAPI.class)) {
                    System.out.println("test2 禁止调用");
                }
                else {
                    System.out.println("test2 正常调用");
                }
            } catch (NoSuchMethodException | SecurityException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            /*
             // 元素上是否存在指定类型的注解
                public boolean isAnnotationPresent(Class annotationClass) {}
     
             // 元素上如果存在指定类型的注解,则返回Annotation 对象,否则返回 null。
                public  A getAnnotation(Class annotationClass) {}
     
             // 返回此元素上存在的所有注解,包括从父类继承的
             public Annotation[] getAnnotations() {}
     
             // 返回直接存在于此元素上的所有注解,
             // 注意,不包括父类的注解,调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响,没有则返回长度为0的数组。
             public Annotation[] getDeclaredAnnotations() {} 
            */
            if(TestAnnotation.class.isAnnotationPresent(MyAnnotation.class)) {
                //元素上存在一个注解类型,则返回一个注解对象
                MyAnnotation annotation = (MyAnnotation)TestAnnotation.class.getAnnotation(MyAnnotation.class);
                System.out.println("----------------->annotation = "+annotation);
                System.out.println("----------------->annotation.id = "+annotation.id());
                System.out.println("----------------->annotation.name = "+annotation.name());
                System.out.println("----------------->annotation.test = "+annotation.test());
                //对于method的注解可以改变method的执行流程
                test1();
                test2();
                
                System.out.println(s1);
                System.out.println(s2);
                
                SetFieldofTestAnnotation.changeFieldofTestAnnotation(); //注解+反射
                //要修改的值通过注解得到,然后通过反射来修改这些值
                
                System.out.println(s1);
                System.out.println(s2);
                
            }
        }
    
    }
    
    ----------------->annotation = @MyAnnotation(name=weijuncheng, id=2, test=)
    ----------------->annotation.id = 2
    ----------------->annotation.name = weijuncheng
    ----------------->annotation.test = 
    test1 禁止调用
    test2 正常调用
    o1
    o2
    s1
    s2
    

    从中可以看到获取注解的方法,通过注解改变执行方式,通过注解提供的值+反射来改变类中的私有属性(常见)

    注解的使用场景

    注解有许多用处,主要如下:
    提供信息给编译器: 编译器可以利用注解来探测错误和警告信息
    编译阶段时的处理: 软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。
    运行时的处理: 某些注解可以在程序运行的时候接受代码的提取
    值得注意的是,注解不是代码本身的一部分。注解只是某些工具的的工具。注解主要针对的是编译器和其它工具软件(SoftWare tool)。当开发者使用了Annotation 修饰了类、方法、Field 等成员之后,这些 Annotation 不会自己生效,必须由开发者提供相应的代码来提取并处理 Annotation 信息。这些处理提取和处理 Annotation 的代码统称为 APT(Annotation Processing Tool)

    工作中遇到

    CTS框架就用来这个方法,将提供的属性值(config文件)通过注解的方式改变框架中相应数据结构中的值,具体见OptionSetter.java

    延伸学习

    注解应用实例

    • butterknife 基于Android的视图依赖注入框架,其原理是使用编译时处理注解生成相关辅助代码,在运行时进行辅助类的加载从而调用相关方法完成视图的注入。
    • Dagger2 依赖注入的框架,把类实例的初始化过程都挪至一个容器中统一管理,具体需要哪个实例时,就从这个容器取出。原理是通过注解建立起实例需求端,实例容器,实例供应端的对应关系,在运行时将初始化好的实例注入到需求端。
    • EventBus 一个简化Andorid、Fragment、Threads、Service之间信息传递的一个发布/订阅事件集。原理是在运行时通过注解将发布的消息,分发给各个订阅者。
    • JUnit4 Java单元测试框架

    总结

    个人理解:注解有点类似于接口和抽象类的结合,一般用于很多类共享的属性和方法,节省工作效率;目前一般都用在框架工具的开发上

    相关文章

      网友评论

          本文标题:java注解学习

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