注解(Annotation)至关于一种标记,在程序中加入注解就等于为程序打上某种标记,没有加,则等于没有任何标记,之后,javac编译器、开发工具和其余程序能够经过反射来了解你的类及各类元素上有无何种标记,看你的程序有什么标记,就去干相应的事,标记能够加在包、类,属性、方法,方法的参数以及局部变量上。java
注解就至关于一个你的源程序要调用一个类,在源程序中应用某个注解,得事先准备好这个注解类。就像你要调用某个类,得事先开发好这个类。程序员
注解根据它的生命周期能够分为,源码注解、编译时注解、运行时注解数组
源码注解:只存在于源码中,当源码进行编译之后,注解就不存在了ide
编译时注解:存在于源码和字节码文件中,当程序开始运行注解就不存在了工具
运行时注解:不只存在于源码和字节码文件中,在程序运行时依然存在,而且能够影响程序的运行
开发工具
按照运行机制分spa
源码注解:注解只在源码中存在,编译成.class文件就不存在了;code
编译时注解:注解在源码和.class文件中都存在;对象
运行时注解:在运行阶段还起做用,甚至会影响运行逻辑的注解;blog
按照来源分
JDK注解
第三方注解
自定义注解
注解(Annotation)很重要,将来的开发模式都是基于注解的,JPA是基于注解的,Spring2.5以上都是基于注解的,Hibernate3.x之后也是基于注解的,如今的Struts2有一部分也是基于注解的了,注解是一种趋势,如今已经有很多的人开始用注解了,注解是JDK1.5以后才有的新特性
JDK1.5以后内部提供的三个注解
@Deprecated 意思是“废弃的,过期的”
@Override 意思是“重写、覆盖”
@SuppressWarnings 意思是“压缩警告”
1 @Documented 2 @Retention(value=RUNTIME) 3 public @interface Deprecated
Java API中是这样定义的@Override的
1 @Target(value=METHOD) 2 @Retention(value=SOURCE) 3 public @interface Override
Java API中是这样定义的@SuppressWarnings的
1 @Target(value={TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE}) 2 @Retention(value=SOURCE) 3 public @interface SuppressWarnings
@SuppressWarnings是给javac(java编译器)看的,编译器编译完java文件后,@SuppressWarnings注解就没有用了,因此@SuppressWarnings的Retention的属性值是RetentionPolicy.SOURCE
package com.my.zhujiedemo; public class FuLei { public void print(){ System.out.println("我是父类中的方法"); } }
package com.my.zhujiedemo; public class A extends FuLei{ @Override public void print() { System.out.println("我重写了父类中的方法"); } @Deprecated public void say(){ System.out.println("我是过期的方法"); } @SuppressWarnings("depracation") public void speak(){ System.out.println("我是没过期的方法"); } }
package com.my.zhujiedemo; public class ZhujieDemo { public static void main(String[] args) { A a = new A(); a.print(); a.say(); a.speak(); } }
在这出不来效果,我截图表示,以图为主
运行结果: 我重写了父类中的方法 我是过期的方法 我是没过期的方法
注解的定义及应用
注解经过 @interface关键字进行定义。 public @interface AnnotationZhuJie { }
它的形式跟接口很相似,不过前面多了一个 @ 符号。上面的代码就建立了一个名字为 AnnotaionZhuJie 的注解。
你能够简单理解为建立了一张名字为 AnnotationZhuJie 的标签
上面建立了一个注解,那么注解的的使用方法是什么呢?
@AnnotationZhuJie public class Test { }
当咱们发现JDK为咱们提供的注解并不能知足咱们的某些需求的时候,好比,想要让程序在运行的时候得到信息,这时候,咱们就须要使用一个工具来制做符合咱们需求的注解,这个工具叫作元注解。
元注解:给注解进行注解的注解
元标签有 @Retention、@Documented、@Target、@Inherited、@Repeatable 5 种
Retention 的英文意为保留期的意思。当 @Retention 应用到一个注解上的时候,它解释说明了这个注解的的存活时间。
它的取值以下:
RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
RetentionPolicy.RUNTIME 注解能够保留到程序运行的时候,它会被加载进入到 JVM 中,因此在程序运行时能够获取到它们。
咱们能够这样的方式来加深理解,@Retention 去给一张标签解释的时候,它指定了这张标签张贴的时间。@Retention 至关于给一张标签上面盖了一张时间戳,时间戳指明了标签张贴的时间周期。
@Retention(RetentionPolicy.RUNTIME) public @interface AnnotationZhuJie { }
顾名思义,这个元注解确定是和文档有关。它的做用是可以将注解中的元素包含到 Javadoc 中去。
你能够这样理解,当一个注解被 @Target 注解时,这个注解就被限定了运用的场景。
@Target元注解决定了一个注解能够标识到哪些成分上,如标识在在类身上,或者属性身上,或者方法身上等成分,@Target默认值为任何元素(成分)
类比到标签,本来标签是你想张贴到哪一个地方就到哪一个地方,可是由于 @Target 的存在,它张贴的地方就很是具体了,好比只能张贴到方法上、类上、方法参数上等等。@Target 有下面的取值
ElementType.ANNOTATION_TYPE 能够给一个注解进行注解
ElementType.CONSTRUCTOR 能够给构造方法进行注解
ElementType.FIELD 能够给属性进行注解
ElementType.LOCAL_VARIABLE 能够给局部变量进行注解
ElementType.METHOD 能够给方法进行注解
ElementType.PACKAGE 能够给一个包进行注解
ElementType.PARAMETER 能够给一个方法内的参数进行注解
ElementType.TYPE 能够给一个类型进行注解,好比类、接口、枚举
1 @Target(value={TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE}) 2 @Retention(value=SOURCE) 3 public @interface AnnotationZhuJie
Inherited 是继承的意思,可是它并非说注解自己能够继承,而是说若是一个超类被 @Inherited 注解过的注解进行注解的话,那么若是它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。
说的比较抽象。代码来解释。
@Inherited @Retention(RetentionPolicy.RUNTIME) @interface Test {} @Test public class A {} public class B extends A {}
注解 Test 被 @Inherited 修饰,以后类 A 被 Test 注解,类 B 继承 A,类 B 也拥有 Test 这个注解。
Repeatable 天然是可重复的意思。@Repeatable 是 Java 1.8 才加进来的,因此算是一个新的特性。
什么样的注解会屡次应用呢?一般是注解的值能够同时取多个。
举个例子,一我的他既是程序员又是产品经理,同时他仍是个画家。
@interface Persons { Person[] value(); } @Repeatable(Persons.class) @interface Person{ String role default ""; } @Person(role="artist") @Person(role="coder") @Person(role="PM") public class SuperMan{ }
注意上面的代码,@Repeatable 注解了 Person。而 @Repeatable 后面括号中的类至关于一个容器注解。
什么是容器注解呢?就是用来存放其它注解的地方。它自己也是一个注解。
咱们再看看代码中的相关容器注解。
@interface Persons { Person[] value(); }
按照规定,它里面必需要有一个 value 的属性,属性类型是一个被 @Repeatable 注解过的注解数组,注意它是数组。
若是很差理解的话,能够这样理解。Persons 是一张总的标签,上面贴满了 Person 这种同类型但内容不同的标签。把 Persons 给一个 SuperMan 贴上,至关于同时给他贴了程序员、产品经理、画家的标签。
咱们可能对于 @Person(role=“PM”) 括号里面的内容感兴趣,它其实就是给 Person 这个注解的 role 属性赋值为 PM ,你们不明白正常,立刻就讲到注解的属性这一块。
注解能够当作是一种特殊的类,既然是类,那天然能够为类添加属性
注解的属性也叫作成员变量。注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。
若是一个注解中有一个名称为value的属性,且你只想设置value属性(即其余属性都采用默认值或者你只有一个value属性),那么能够省略掉“value=”部分。
package com.my.zhujie; public class Aaa { public void print(){ System.out.println("A中的方法"); } }
package com.my.zhujie; @Anno(name="小花",sex="女") public class Bbb extends Aaa { @Override @Anno(name="小明",sex="男",age=30) public void print() { super.print(); } }
package com.my.zhujie; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; //注解的做用域 @Target({ElementType.METHOD,ElementType.TYPE}) //注解的生命周期 @Retention(RetentionPolicy.RUNTIME) public @interface Anno { //成员以无参无异常的方式声明 String name(); String sex(); //能够用default为成员指定一个默认值 int age() default 20; }
package com.my.zhujie; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class ZhuJie { public static void main(String[] args) { try { Class c = Class.forName("com.my.zhujie.Bbb"); Bbb b = (Bbb)c.newInstance(); Method method = c.getMethod("print"); method.invoke(b); //得到类上的注解 boolean b1 = c.isAnnotationPresent(Anno.class); if (b1){ //得到注解对象 Anno a = (Anno)c.getAnnotation(Anno.class); //打印注解成员的值 System.out.println(a.name()); System.out.println(a.sex()); System.out.println(a.age()); } //得到方法上的注解 Anno anno = method.getAnnotation(Anno.class); System.out.println(anno.name()); System.out.println(anno.sex()); System.out.println(anno.age()); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } }
运行结果: A中的方法 小花 女 20 小明 男 30
若是一个注解内仅仅只有一个名字为 value 的属性时,应用这个注解时能够直接接属性值填写到括号内。
//注解的做用域 @Target({ElementType.METHOD,ElementType.TYPE}) //注解的生命周期 @Retention(RetentionPolicy.RUNTIME) public @interface Anno { String value(); }
上面代码中,Check 这个注解只有 value 这个属性。因此能够这样应用。
package com.my.zhujie; @Anno("值") public class Bbb extends Aaa { @Override @Anno("值") public void print() { super.print(); } }
最后,还须要注意的一种状况是一个注解没有任何属性。好比
@Target({ElementType.METHOD,ElementType.TYPE}) //注解的生命周期 @Retention(RetentionPolicy.RUNTIME) public @interface Anno { }
那么在应用这个注解的时候,括号均可以省略。
package com.my.zhujie; @Anno public class Bbb extends Aaa { @Override @Anno public void print() { super.print(); } }