注解(Annotation)的入门与使用
Annotation 是J2SE 1.5的新特性,通过使用注解,开发人员可以在不改变原有逻辑的情况下,在源文件中加入一些补充的信息。(这些信息主要是告知编译器或者通知程序开发者一些信息)这些信息将不会影响程序运行,但在如果编写的代码不符合注解的要求,则编译器将会发出警告,IDE工具也会对应不同的注解在代码中标示相应的警告。
我把Annotation分为两部分来归纳:一、java本身提供的Annotation二、用户自己定义的Annotation无论是哪种Annotation,都是默认实现了java.lang.annotation.Annotaton接口的。
一、系统内建的Annotation
java本身提供了以下三个Annotation供用户使用:
1: @Override此注解主要是在覆写方法时使用,用于保证方法覆写的正确,如果没有覆写正确,则会提出警告。(相当于告诉编译器:我现在要覆写父类的这个方法了,如果没写对,要提示我!)以下通过一段代码来直观了解@Override的作用:
package cn.hxq.annotationtest; public class OverrideTest { public static void main(String[] args){ Person p = new Student(); System.out.println(p.getInfo()); } } class Person{ public String getInfo(){ return "This is class Person."; } } class Student extends Person{ @Override public String getinfo(){ return "This is class Student."; } }
标记@Override 注解后,子类Student的getinfo方法名写错了,此时编译器就会报错。如果覆写正确,就可以正常通过。如果没有加入注解,编译器会认为这个getinfo方法是Student自己定义的新方法,而不是覆写Person类继承来的getInfo方法。那编译器就会通过,但以后要用的时候,我们想要的是覆写父类之后的方法,但却使用了一个在子类新定义的方法,给调试带来很大困难。
2:@Deprecated用于声明一个不建议使用的方法。(告诉用户这方法过时了,最好不要用,但非要使用的话还是可以的)例子:
public class OverrideTest { public static void main(String[] args){ Person p = new Student(); System.out.println(p.getInfo()); } } class Person{ @Deprecated //加入过时注解 public String getInfo(){ return "This is class Person."; } }
声明了Person类的getInfo()方法为@Deprecated,在命令行编译时会有警告信息。如果使用eclipse等IDE时,在写源代码的时候就会在getInfo()上划横线。
3: @SuppressWarnings用来压制警告。
例子:
package cn.hxq.annotationtest; public class SuperssWarningTest { @SuppressWarnings("unchecked") public static void main(String[] args) { Demo d = new Demo(); d.setVar("hxq"); } } class Demo<T>{ private T var; public T getVar(){ return var; } public void setVar(T var){ this.var = var; } }
由于使用了泛型的类Demo,却没有在实例化时明确指定泛型的具体类型,导致编译器发出警告,但在主方法前使用了@SuppressWarnings注解,编译时就不会发出警告。
还可以同时压制多条警告:如:
package cn.hxq.annotationtest; public class SuperssWarningTest { @SuppressWarnings({"unchecked","deprecation"}) public static void main(String[] args) { Demo d = new Demo(); d.setVar("hxq"); } } @Deprecated class Demo<T>{ private T var; public T getVar(){ return var; } public void setVar(T var){ this.var = var; } }
此时压制了2条警告。关于SuppressWarning 可以压制的警告类型有很多,可以参考API文档。
二、定义自己的Annotation
要定义一个自己的Annotation,和定义自己的接口相似。
如:HxqAnnotation.java
package cn.hxq.annotationtest; import java.lang.annotation.*; //元注解 @Retention(RetentionPolicy.RUNTIME) //用于表示HxqAnnotation可以存活到运行时 @Target({ElementType.METHOD,ElementType.TYPE})//用于表示HxqAnnotation可以标示在什么地方 //以下是定义自己的注解类 @interface HxqAnnotation { }
这个自己定义的注解包括两部分:1、元注解部分:@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD,ElementType.TYPE})这两个都是元注解,也就是注解的注解,元注解可以表明我们自己编写的注解的一些状态。
@Retention注解:给自己定义的@interface HxqAnnotation注解加上一个注解,用解释HxqAnnotation注解能存活到哪个生命周期。关于注解的生命周期: 1、(SOURCES)java源文件(*.java) ---编译器编译(javac)--->2、(CLASS)class文件(*.class在硬盘上) ---类加载器将class文件加载到内存--->(RUNTIME)class字节码(*.class在内存中)对应了3个周期:1、RetentionPolicy.SOURCES (能活到源文件时)2、RetentionPolicy.CLASS (能活到在硬盘上生成的class文件时)3、RetentionPolicy.RUNTIME (能活到类加载器将class文件加载到内存后,也就是运行时)
可以看出RetentionPolicy是一个enum类型,它对应的3个常量用于标示注解能存活到哪个周期。@Retention(RetentionPolicy.RUNTIME)就表示了HxqAnnotation能活到运行时。
@Target注解:用于表示一个注解能在一个类的哪个成分上使用。@Target(ElementType.METHOD) 表示该注解可以在一个类型的方法中使用。括号中ElementType也是一个enum类型,它的其他常量有:
ElementType.TYPE :表示该注解可以在类、接口(包括注释类型)或枚举上声明。ElementType.ANNOTATION_TYPE : 表示该注解只可以注释类型上声明。ElementType.FIELD : 表示该注解可以在成员变量上使用。ElementType.PARAMETER :表示该注解可以在参数上使用。ElementType.CONSTRUCTOR : 表示该注解可以在构造方法上使用。ElementType.LOCAL_VARIABLE : 表示该注解可以在局部变量上使用。ElementType.PACKAGE :表示该注解可以在包名上使用。
了解了以上的元注解,现在可以看懂自己定义的注解了:
package cn.hxq.annotationtest; import java.lang.annotation.*; //元注解 @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD,ElementType.TYPE}) //以下是定义自己的注解类 @interface HxqAnnotation { };
写一个测试注解的类:AnnotationTest.java:
AnnotationTest.java: package cn.hxq.annotationtest; //此类使用了自己编写的HxqAnnotation,@HxqAnnotation就是该类的一个实例对象 @HxqAnnotation public class AnnotationTest { @HxqAnnotation public static void main(String[] args) { // TODO Auto-generated method stub //在使用Annotation的类中利用反射操作得到Annotation if(AnnotationTest.class .isAnnotationPresent(HxqAnnotation.class)){//判断指定的Annotation在不在 //如果该注解存在,则使用反射得到并打印 HxqAnnotation hxq = (HxqAnnotation)AnnotationTest.class.getAnnotation(HxqAnnotation.class); System.out.println(hxq); }else{ System.out.println("该注解不存在!"); } } };
这个类只有一个main方法,在类名之上声明过@HxqAnnotation ,在main方法之上也可以声明@HxqAnnotation。@HxqAnnotation就是HxqAnnotation的一个实例对象。并且在HxqAnnotation中使用了@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD,ElementType.TYPE})两个元注解,让@HxqAnnotation既可以在类上声明,也可以在方法上声明,并且可以存活到运行时。那么这个时候,就可以通过反射来得到这个注解的内容。很明显,现在自己定义的HxqAnnotation除了名字和元注解之外,什么都没有...
下面来给自定义的注解添加元素:
package cn.hxq.annotationtest; import java.lang.annotation.*; //元注解 @Retention(RetentionPolicy.RUNTIME) //用于注释HxqAnnotation可以存活到运行时 @Target({ElementType.METHOD,ElementType.TYPE})//用于注释HxqAnnotation可以标示在什么地方 //以下是定义自己的注解类 @interface HxqAnnotation { //和接口类似,注解的属性类似方法的声明,并且默认是public abstract的 //同时给color属性给定了缺省值 String color() default "BULE"; /*value 属性是个特殊的属性,如果只有value属性或其他属性均有缺省值的 的情况下,可以使用"XXX"的方式直接赋值*/ String value(); //为注解添加一个数组类型的属性: int[] arrayAttr() default {2,3,4}; //为注解添加一个注解类型的属性: MateAnnotation annotationAttr() default @MateAnnotation("hxq"); //为注解添加一个Class类型的属性 Class<?> classAttr() default HxqAnnotation.class; //缺省值是自己的class }
在其他类中测试注解类的使用效果:
package cn.hxq.annotationtest; //此类使用了自己编写的HxqAnnotation,@HxqAnnotation就是该类的一个实例对象 //为注解的各种属性赋值产生实例对象: @HxqAnnotation(color = "RED",value = "abc",arrayAttr = 1, annotationAttr =@MateAnnotation("hxq")) public class AnnotationTest { /** * @param args */ @HxqAnnotation("abc")//其他的值缺省了,可以使用这样的方式给value属性赋值 public static void main(String[] args) { // TODO Auto-generated method stub //在使用Annotation的类中利用反射操作得到Annotation if(AnnotationTest.class .isAnnotationPresent(HxqAnnotation.class)){//判断指定的Annotation在不在 //如果该注解存在,则使用反射得到并打印 HxqAnnotation hxq = (HxqAnnotation)AnnotationTest.class.getAnnotation(HxqAnnotation.class); System.out.println(hxq.color()); //打印color属性 System.out.println(hxq.value()); //打印value属性 System.out.println(hxq.arrayAttr().length); //打印arrayAttr属性,只有1个元素,值为1 System.out.println(hxq.annotationAttr());//打印annotationAttr属性,此属性又是注解类型,里面只有一个值value System.out.println(hxq.classAttr().getName()); //打印classAttr属性 }else{ System.out.println("该注解不存在!"); } } }
*关于注解的详细语法可以看java语言规范,即 language specification
