注解

一、注解的定义

1.注解(Annotation),也叫元数据。它是jdk1.5后引入的一个新的特性。与类,接口,枚举是同一个层次。可以声明在类、字段、方法、局部变量、方法参数等的前面。注解也属于一种类型,有自己的语法

1 @Target(ElementType.METHOD)
2 @Retention(RetentionPolicy.RUNTIME)
3 public @interface TestAnnotation {
4 
5 }

如上所示,声明一个注解很简单,使用@interface声明TestAnnotation注解,并且使用两个java内置元注解(标识其他注解的注解)进行修饰。

二、java中常见的注解

1.元注解

元注解专职负责注解其他注解,主要有下面四种:

  • @Target,主要用来约束注解可以使用的地方(如方法、类或字段)
  • @Retention,主要用来约束注解的生命周期,分别有三个值,源码级别(source),类文件级别(class)或者运行时级别(runtime),主要区别:RetentionPolicy.SOURCE:注解将被编译器丢弃(该类型的注解信息只会保留在源码里,源码经过编译后,注解信息会被丢弃,不会保留在编译好的class文件里)RetentionPolicy.CLASS:注解在class文件中可用,但会被VM丢弃(该类型的注解信息会保留在源码里和class文件里,在执行的时候,不会加载到虚拟机中),请注意,当注解未定义Retention值时,默认值是CLASS,如Java内置注解,@Override、@Deprecated、@SuppressWarnning等。RetentionPolicy.RUNTIME:注解信息将在运行期(JVM)也保留,因此可以通过反射机制读取注解的信息(源码、class文件和执行的时候都有注解的信息),如SpringMvc中的@Controller、@Autowired、@RequestMapping等。
  • @Document,将此注解包含在Jacadoc中
  • @Inherited,允许子类继承父类的注解

2.标准注解

  • @Deprecated,Deprecated是弃用的意思,顾名思义这个注解用来标识过时的元素,例如过时的类,方法或者成员变量。
  • @Override,这可能是我们最熟悉的注解之一了,表示被注解的方法是复写了当前类父类的方法。
 1 public class Demo{
 2     @Deprecated
 3     public void test(){
 4         System.out.println("我已经被弃用");
 5     }
 6     @Override
 7     public boolean equals(Object obj) {
 8         return super.equals(obj);
 9     }
10 }        
  • @SuppressWarnings,主要用来忽略各种警告用的。用于有选择的关闭编译器对类、方法、成员变量、变量初始化的警告,使用如下:
public class Demo2 {
  @SuppressWarnings("deprecation")
    public void test2(){
        Demo demo = new Demo();
        demo.test();//当使用@SuppressWarnings("deprecation")后,警告消失
    }

}    

上例只是SuppressWarnings的一种,以上三种注解的详细使用说明可以参考源码。例如SuppressWarnnings注解的源码如下:

 1 @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
 2 @Retention(RetentionPolicy.SOURCE)
 3 public @interface SuppressWarnings {
 4 String[] value();
 5 /*
 6 deprecation:使用了不赞成使用的类或方法时的警告;
 7 unchecked:执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型; 
 8 fallthrough:当Switch 程序块直接通往下一种情况而没有 Break 时的警告;
 9 path:在类路径、源文件路径等中有不存在的路径时的警告; 
10 serial:当在可序列化的类上缺少 serialVersionUID 定义时的警告; 
11 finally:任何 finally 子句不能正常完成时的警告; 
12 all:关于以上所有情况的警告。
13 */
14 }

3.注解元素

1).注解元素可用的类型如下所示:

所有的基本类型(int,float,boolean等等)

  • String
  • Class
  • enum
  • Annotation
  • 以上类型的数组

2).注解元素的默认值限制

编译器对注解元素的默认值有些过分的挑剔,元素不能有不确定的值,即元素必须要有默认值,要么在使用注解的时候提供元素的值。其次对于非基本类型的元素,无论是源码中声明时,或是注解接口中定义默认值的时候,不能以null作为其默认值。在注解的声明中,所有的元素都存在,并且具有其值,可以赋予空字符串或者负数,如下:

1 @Target(ElementType.METHOD)
2 @Retention(RetentionPolicy.RUNTIME)
3 public @interface SimplDemo {
4     public int id() default -1;
5     public String description() default "";//元素的定义类似接口中方法的定义
6     
7 }

注意:不能使用关键字extends来继承某个@interface.

二、自定义注解的使用

1.自定义注解的简单使用

自定义一个注解TestAnnotation,主要用在方法上,首先定义一个注解,如下所示:

1 @Target(ElementType.METHOD)
2 @Retention(RetentionPolicy.RUNTIME)
3 public @interface TestAnnotation {
4     String name() ;
5     String value();
6 }

 定义注解的使用如下:

 1 public class Demo {
 2     @TestAnnotation(name = "test", value = "zz")
 3     public void test() {
 4         System.out.println("被注解方法执行完毕");
 5     }
 6 
 7     public void test2() {
 8 
 9     }
10 
11     public static void main(String[] args) {
12         Demo demo = new Demo();
13         Class<?> clz = demo.getClass();
14         Method[] methods = clz.getDeclaredMethods();
15         for (Method method : methods) {
16             // 遍历Demo类定义的所有方法,如果方法被TestAnnotation注解,则执行方法,并获取注解的相关属性
17             if (method.isAnnotationPresent(TestAnnotation.class)) {
18                 method.setAccessible(true);
19                 try {
20                     method.invoke(demo, null);
21                 } catch (Exception e) {
22                     e.printStackTrace();
23                 }
24 
25                 Annotation[] annotations = method.getAnnotations();
26 
27                 for (Annotation annotation : annotations) {
28                     System.out.println(annotation);
29                     if (annotation instanceof TestAnnotation) {
30                         TestAnnotation testAnnotation = (TestAnnotation) annotation;
31                         System.out.println(testAnnotation.name() + "--"
32                                 + testAnnotation.value());
33                     }
34                 }
35 
36             }
37         }
38 
39     }
40 
41 }

结果输出:

被注解方法执行完毕
@com.demo.TestAnnotation(name=test, value=zz)
test--zz

注意:当TestAnnotation注解的保留策略是RetentionPolicy.CLASS和RetentionPolicy.SOURCE时,通过反射是获取不到注解信息的。

通常对注解的使用,都是结合反射一起,如上通过反射和注解可以决定是都执行Demo对象的某个方法。上述例子也只是对注解的一个简单的使用,注解带来的好处和用法太多。

主要有下面三方面:
-提供信息给编译器:编译器可以利用注解来探测错误和警告信息
-编译阶段时的处理:软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。
-运行时的处理: 某些注解可以在程序运行的时候接受代码的提取

三、注解与反射

有句话叫"纸上得来终觉浅,绝知此事要躬行",可能对注解还是一知半解,很多东西都是从不懂到懂,需要一个过程,可能这个过程是痛苦的,不过只要坚持,迟早会懂。等到了那一天反而会觉得很简单。通过实际的代码去理解理论知识,是最好的途径,特别是当在工作中使用了后就更有体会。下面这个例子是java编程思想中的一个经典的例子,能够帮助很好的理解注解和反射的关系。

 1 @Target(ElementType.TYPE)//用于注解类
 2 @Retention(RetentionPolicy.RUNTIME)
 3 public @interface DBTable {
 4     public String name() default "";
 5 }
 6 
 7 --------------------------------------------------------------
 8 @Target(ElementType.FIELD)
 9 @Retention(RetentionPolicy.RUNTIME)
10 public @interface Constraints {
11     boolean primaryKey() default false;
12     boolean allowNull() default true;
13     boolean unique() default false;
14 }
15 
16 ---------------------------------------------------------------
17 @Target(ElementType.FIELD)
18 @Retention(RetentionPolicy.RUNTIME)
19 public @interface SQLString {
20     int value() default 0;
21     String name() default "";
22     Constraints constraints() default @Constraints;
23 
24 }
25 
26 ----------------------------------------------------------------
27 @Target(ElementType.FIELD)
28 @Retention(RetentionPolicy.RUNTIME)
29 public @interface SQLInteger {
30     String name() default "";
31     Constraints constraints() default @Constraints;
32 }

应用上述自定义注解的类

 1 @DBTable(name="MEMBER")
 2 public class Member {
 3     @SQLString(30)
 4     String firstName;
 5     @SQLString(50)
 6     String lastName;
 7     @SQLInteger
 8     Integer age;
 9     @SQLString(value=30,constraints=@Constraints(primaryKey=true,allowNull=false,unique=true))
10     String handle;
11     static int memberCount;
12     
13     public String getHandle(){
14         return handle;
15     }
16     public String getFirstName(){
17         return firstName;
18     }
19     public String getLastName(){
20         return lastName;
21     }
22     public String toString(){
23         return handle;
24     }
25     public Integer getAge(){
26         return age;
27     }
28 }

实现处理器,利用反射和注解实现自动生产创建数据库表的SQL语句

 1 /**
 2  * 注解处理器
 3  *    
 4  */
 5 public class TableCreator {
 6     public static void main(String[] args)throws Exception {
 7         Class<?> clz = Class.forName("com.sun.lp.demo.Member");
 8         
 9         DBTable dbTable= clz.getAnnotation(DBTable.class);//获取DBTable注解
10         if (dbTable==null) {
11             System.out.println("no DBTable annotation in class Member");
12             System.exit(0);
13         }
14         
15         String tableName = dbTable.name();
16         if(tableName.length()<1){
17             tableName=clz.getName().toUpperCase();
18         }
19         List<String> list = new ArrayList<String>();
20         String tableCreate =null;
21         for(Field field:clz.getDeclaredFields()){
22             String colName=null;
23             Annotation [] annotations = field.getAnnotations();
24             if(annotations.length<1)
25                 continue;
26             if(annotations[0] instanceof SQLInteger){
27                 SQLInteger sInt = (SQLInteger) annotations[0];
28                 if(sInt.name().length()<1)
29                     colName = field.getName().toUpperCase();
30                 else 
31                     colName = sInt.name();
32                 list.add(colName+" INT"+getConstraints(sInt.constraints()));
33             }
34             if(annotations[0] instanceof SQLString){
35                 SQLString sString = (SQLString) annotations[0];
36                 if(sString.name().length()<1)
37                     colName = field.getName().toUpperCase();
38                 else 
39                     colName = sString.name();
40                 list.add(colName+" VARCHAR("+sString.value()+")"+getConstraints(sString.constraints()));
41             }
42             
43         }
44         StringBuilder createCommand = new StringBuilder("CREATE TABLE "+tableName+"(");
45         for(String colDef : list)
46             createCommand.append("
   "+colDef+",");
47         tableCreate = createCommand.substring(0,(createCommand.length()-1))+");";
48         System.out.println(tableCreate);
49     }
50     
51     private static String getConstraints(Constraints con){
52         String constraints = "";
53         if(!con.allowNull())
54             constraints+=" NOT NULL";
55         if(con.primaryKey())
56             constraints+=" PRIMARY KEY";
57         if(con.unique())
58             constraints+=" UNIQUE";
59         
60         return constraints;
61         
62     }
63 
64 }

结果输出:

CREATE TABLE MEMBER(
   FIRSTNAME VARCHAR(30),
   LASTNAME VARCHAR(50),
   AGE INT,
   HANDLE VARCHAR(30) NOT NULL PRIMARY KEY UNIQUE);
原文地址:https://www.cnblogs.com/liupiao/p/9273664.html