java注解学习

前言

注解(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注解学习)