程序员人生 网站导航

注解(一)——注解入门

栏目:php教程时间:2016-12-06 11:04:32

注解


Annotation(注解)概述

从JDK5.0开始, Java增加了对元数据(MetaData)的支持,也就是 Annotation(注解)。
Annotation其实就是代码里的特殊标记,它用于替换配置文件,也就是说,传统方式通过配置文件告知类如何运行,有了注解技术后,开发人员可以通过注解告知类如何运行。在Java技术里注解的典型利用是:可以通过反射技术去得到类里面的注解,以决定怎样去运行类
掌握注解技术的要点:

  • 如何定义注解。
  • 如何反射注解,并根据反射的注解信息,决定如何去运行类。

了解注解及java提供的几个基本注解

  • @SuppressWarnings:抑制编译器正告。
    首先编写1个AnnotationTest类,先通过@SuppressWarnings的利用让大家认识和了解1下注解,通过System.runFinalizersOnExit(true);的编译正告引出@SuppressWarnings("deprecation")

    public class AnnotationTest { @SuppressWarnings("deprecation") public static void main(String[] args) { System.runFinalizersOnExit(true); } }
  • @Deprecated:用于表示某个程序元素(类,方法等)已过时。
    接着直接在刚才的类中增加1个方法,并加上@Deprecated标注,在另外1个类中调用这个方法。

    public class AnnotationTest { @SuppressWarnings("deprecation") public static void main(String[] args) { System.runFinalizersOnExit(true); } @Deprecated public static void sayHello() { System.out.println("hi,李阿昀"); } }
  • @Override:限定重写父类方法,该注解只能用于方法。
    表示1个方法声明打算重写超类中的另外一个方法声明。如果方法利用此注释类型进行注解但没有重写超类方法,则编译器会生成1条毛病消息。
    public boolean equals(Reflect other)方法与HashSet结合讲授。

总结:注解相当于1种标记,在程序中加了注解就等于为程序打上了某种标记,没加,则等于没有某种标记,以后,javac编译器,开发工具和其他程序可以用反射来了解你的类及各种元素上有没有何种标记,看你有甚么标记,就去干相应的事。标记可以加在包,类,字段,方法,方法的参数和局部变量上

注解的利用结构图

注解就相当于1个你的源程序中要调用的1个类,要在源程序中利用某个注解,得先准备好了这个注解类,就像你要调用某个类,得先要开发好这个类。
这里写图片描述

自定义注解及其利用

定义新的Annotation类型使用@interface关键字。

  • 定义1个最简单的注解:

    public @interface ItcastAnnotation { }
  • 把它加在某个类上:

    @ItcastAnnotation public class AnnotationTest { @SuppressWarnings("deprecation") public static void main(String[] args) { System.runFinalizersOnExit(true); } @Deprecated public static void sayHello() { System.out.println("hi,李阿昀"); } }
  • 用反射进行测试AnnotationTest的定义上是不是有@ItcastAnnotation:

    @ItcastAnnotation public class AnnotationTest { @SuppressWarnings("deprecation") public static void main(String[] args) { System.runFinalizersOnExit(true); if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)) { // 类上是不是有注解,默许情况下返回false ItcastAnnotation annotation = (ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class); System.out.println(annotation); } } @Deprecated public static void sayHello() { System.out.println("hi,李阿昀"); } }

    发现控制台无任何反应,根据反射测试的问题,引出@Retention元注解的讲授。

  • @Retention元注解
    只能用于修饰1个Annotation的定义, 用于指定该Annotation可以保存的域,@Rentention包括1个RetentionPolicy类型的成员变量,通过这个变量指定域。其有3种取值:

    • RetetionPolicy.SOURCE:编译器直接抛弃这类策略的注解。
    • RetetionPolicy.CLASS:编译器将把注解记录在class文件中,当运行Java程序时,JVM不会保存注解。这是默许值。
    • RetetionPolicy.RUNTIME:编译器将把注解记录在class文件中。当运行Java程序时,JVM会保存注解,程序可以通过反射获得该注解。

    分别对应:java源文件→class文件→内存中的字节码。也即1个Java类具有3种状态:

    编译 JVM装载进内存 .java -------------------> .class -------------------> 运行时(内存里面的java类)

    对@Retention元注解更加细致的解释:

    • 当在java源程序上加入注解,此源程序接着要由javac去编译,javac把源文件编译成.class时,可能会把源程序上的1些注解给去掉,此时.class文件里面就没有注解了,这是1种可能。
    • 假定javac编译器把注解留在.class文件里面,结果我们的程序在使用.class时,要由类加载器把.class文件给调到内存里来,只有把.class文件加载到内存里来以后,类加载器加载完了以后,类加载器会对.class文件进行处理,安全检查等,处理完了以后,在内存中终究的2进制文件才是字节码,类加载器在把.class文件调到内存的进程中,也有转换,转换的时候是不是把.class文件里面的注解保存下来,这也是1种可能。

    所以,1个注解的生命周期有3个阶段。
    我们定义的简单注解修改成以下:

    @Retention(RetentionPolicy.RUNTIME) public @interface ItcastAnnotation { }

    就可以正确打印了。
    结论我们写1个注解的目的,主要是用来替换配置文件,我们希望这个类在运行时取得注解配置的信息,来运行我这个类,想要注解配置的信息能够让类在运行时获得到,那就1定要把这个注解声明在运行时
    思考:@Override、@SuppressWarnings和@Deprecated这3个注解的属性值分别是甚么?
    答:

    @Override→RetetionPolicy.SOURCE
    @SuppressWarnings→RetetionPolicy.SOURCE
    @Deprecated→RetetionPolicy.RUNTIME

  • @Target元注解
    指定注解用于修饰类的哪一个成员。@Target包括了1个名为value,类型为ElementType的成员变量。如若声明注解时,没指定@Target,默许该注解可以作用在类的所有成员上。
    Target的默许值为任何元素,设置Target等于ElementType.METHOD,原来加在类上的注解就报错了,改成用数组方式设置{ElementType.METHOD,ElementType.TYPE}就能够了,表示此注解既可以在方法上使用,也可在类上使用。

  • @Documented元注解
    用于指定被该元Annotation修饰的Annotation类将被javadoc工具提取成文档。
  • @Inherited元注解
    被它修饰的Annotation将具有继承性。如果某个类使用了被@Inherited修饰的Annotation,则其子类将自动具有该注解。

注意:元注解和其枚举属性值不用记,只要会看JDK提供那几个基本注解的API帮助文档的定义或其源代码,按图索骥便可查到,或直接看java.lang.annotation包下面的类。

为注解增加基本属性

注解属性的作用:原来写在配置文件中的信息,可以通过注解的属性进行描写。

  • 甚么是注解的属性
    1个注解相当于1个胸牌,如果你胸前贴了胸牌,就是传智播客的学生,否则,就不是。如果还想辨别出是传智播客哪一个班的学生,这时候候可以为胸牌在增加1个属性来进行辨别。加了属性的标记效果为:@ItcastAnnotation(color="red")
  • 定义基本类型的属性和利用属性
    在注解类中增加String color()

    @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD,ElementType.TYPE}) public @interface ItcastAnnotation { String color(); }

    用反射方式取得注解对应的实例对象后,再通过该对象调用属性对应的方法:

    @ItcastAnnotation(color="red") public class AnnotationTest { @SuppressWarnings("deprecation") public static void main(String[] args) { System.runFinalizersOnExit(true); if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)) { // 类上是不是有注解,默许情况下返回false ItcastAnnotation annotation = (ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class); System.out.println(annotation.color()); // red } } @Deprecated public static void sayHello() { System.out.println("hi,李阿昀"); } }

    可以认为上面这个@ItcastAnnotation(color=”red”)是ItcastAnnotation类的1个实例对象

  • 为属性指定缺省值:

    @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD,ElementType.TYPE}) public @interface ItcastAnnotation { String color() default "blue"; }
  • value属性

    @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD,ElementType.TYPE}) public @interface ItcastAnnotation { String color() default "blue"; String value(); }

    用反射方式取得注解对应的实例对象后,再通过该对象调用属性对应的方法:

    @ItcastAnnotation(color="red", value="abc") public class AnnotationTest { @SuppressWarnings("deprecation") @ItcastAnnotation("xyz") public static void main(String[] args) { System.runFinalizersOnExit(true); if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)) { // 类上是不是有注解,默许情况下返回false ItcastAnnotation annotation = (ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class); System.out.println(annotation.value()); // abc } } @Deprecated public static void sayHello() { System.out.println("hi,李阿昀"); } }

    注意:如果注解中有1个名称为value的属性,且你只想设置value属性(即其他属性都采取默许值或你只有1个value属性),那末可以省略value=部份,例如:@ItcastAnnotation("lhm")

为注解增加高级属性

  • 数组类型的属性

    @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD,ElementType.TYPE}) public @interface ItcastAnnotation { String color() default "blue"; String value(); int[] arrayAttr() default {3,4,4}; }

    用反射方式取得注解对应的实例对象后,再通过该对象调用属性对应的方法:

    @ItcastAnnotation(color="red", value="abc", arrayAttr=1) public class AnnotationTest { @SuppressWarnings("deprecation") @ItcastAnnotation("xyz") public static void main(String[] args) { System.runFinalizersOnExit(true); if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)) { // 类上是不是有注解,默许情况下返回false ItcastAnnotation annotation = (ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class); System.out.println(annotation.arrayAttr().length); // 1 } } @Deprecated public static void sayHello() { System.out.println("hi,李阿昀"); } }

    注意:如果数组属性中只有1个元素,这时候候属性值部份可以省略大括号。

  • 枚举类型的属性

    @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD,ElementType.TYPE}) public @interface ItcastAnnotation { String color() default "blue"; String value(); int[] arrayAttr() default {3,4,4}; EnumTest.TrafficLamp lamp() default EnumTest.TrafficLamp.RED; }

    用反射方式取得注解对应的实例对象后,再通过该对象调用属性对应的方法:

    @ItcastAnnotation(color="red", value="abc", arrayAttr=1) public class AnnotationTest { @SuppressWarnings("deprecation") @ItcastAnnotation("xyz") public static void main(String[] args) { System.runFinalizersOnExit(true); if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)) { // 类上是不是有注解,默许情况下返回false ItcastAnnotation annotation = (ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class); System.out.println(annotation.lamp().nextLamp().name()); // GREEN } } @Deprecated public static void sayHello() { System.out.println("hi,李阿昀"); } }
  • 注解类型的属性:
    我们再定义1个注解MetaAnnotation

    public @interface MetaAnnotation { String value(); }

    接着再定义1个注解类型的属性:

    @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD,ElementType.TYPE}) public @interface ItcastAnnotation { String color() default "blue"; String value(); int[] arrayAttr() default {3,4,4}; EnumTest.TrafficLamp lamp() default EnumTest.TrafficLamp.RED; MetaAnnotation annotationAttr() default @MetaAnnotation("lhm"); }

    枚举和注解都是特殊的类,不能用new创建它们的实例对象,创建枚举的实例对象就是在其中增加元素。在程序中如何创建出1个注解的实例对象啊?直接用@放上1个标记便可,例如:@MetaAnnotation("lhm")
    用反射方式取得注解对应的实例对象后,再通过该对象调用属性对应的方法:

    @ItcastAnnotation(annotationAttr=@MetaAnnotation("flx"), color="red", value="abc", arrayAttr=1) public class AnnotationTest { @SuppressWarnings("deprecation") @ItcastAnnotation("xyz") public static void main(String[] args) { System.runFinalizersOnExit(true); if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)) { // 类上是不是有注解,默许情况下返回false ItcastAnnotation annotation = (ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class); System.out.println(annotation.annotationAttr().value()); // flx } } @Deprecated public static void sayHello() { System.out.println("hi,李阿昀"); } }

    可以认为@ItcastAnnotation是ItcastAnnotation类的1个实例对象,一样的道理,可以认为@MetaAnnotation是MetaAnnotation类的1个实例对象。

注解的详细语法可以通过看java语言规范了解,即看java的language specification,可知道注解的属性类型包括:基本数据类型,String,Class,枚举,其他注解,和这些类型的数组

------分隔线----------------------------
------分隔线----------------------------

最新技术推荐