java提供了一套java注解(Annotation)的机制,与java中的注释不同,注解是一种类型.通过使用注解,可以对方法,类,参数,包,域以及变量等添加标记(即附上某些信息),之后通过反射将标记的信息提取出来以供使用.
?
java.lang.annotation包提供了4种元注解,负责对注解类型进行约束,他们本身也实现了自注解.
@Target:表示该注解用于什么位置,可选的参数是ElementType枚举中的成员:
TYPE 类型声明(类,接口,enum) FIELD 成员变量声明(对象,属性,enum的实例) METHOD 方法声明 PARAMETER 参数声明 CONSTRUCTOR 构造器声明 LOCAL_VARIABLE 局部变量声明 ANNOTATION_TYPE 注解声明 PACKAGE包声明
ElementType在SE1.8中又加入了两个成员:TYPE_PARAMETER,TYPE_USE.
?
@Retention:表示需要在什么级别保存该注解信息(生命周期),可选的参数是RetentionPolicy枚举中的成员:
SOURCE 停留在java源文件,会将被编译器丢弃 CLASS 停留在class文件中,但会被VM丢弃 RUNTIME 内存中的字节码,VM在运行期间保留注解注意,只有声明为RUNTIME,才可以通过反射机制读取注解的信息.
@Document:将注解包含在Javadoc中
@Inherited:允许子类继承父类中的注解,只针对CLASS级别的注解有效
?
?
语法:通过关键字@interface定义,默认继承Annotation接口.注解中通常会有一些元素(),用来表示一些值.这些元素看起来与接口中的方法相似,但是是无参的,并且可以通过default提供默认值,但默认值不能为null.注解中的元素只能是以下类型{所有基本类型,String,Class,Enum,Annotation(但不能是自己),以及以上类型的一维数组形式}.例:
import java.lang.annotation.Target; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface AnnotateDemo { int i(); float f() default 3.14f; char[] c(); String[] s() default { "primitive type", "String", "Class", "annotation", "enumeration", "arrays" }; Class<String> cls() default String.class; Override ovr(); ElementType elmt(); }
注解可以不用元注解进行约束,也可以没有元素,没有元素的注解称为标记注解,例:
public @interface AnnotateDemo { }
默认的ElementType可以是任意类型,RetentionPolicy是CLASS.
?
?
当我们定义了一个注解之后,便可以在允许的位置进行标记,标记时必须对注解内的元素进行赋值,有默认值的元素可以选择性赋值.如果注解只有一个元素且该元素的名称是value的话,在使用注解的时候可以省略"value="直接写需要的值即可.例:
import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) @interface AnnotateDemo { int i(); String s() default "hello"; } public class AnnotateDemo3 { @AnnotateDemo(i = 97) public void a() { } @AnnotateDemo(i = 98, s = "world") public void b() { } public void c() { } public void d() { } }
使用注解最主要的部分在于对注解的处理,那么就会涉及到注解处理器.注解处理器就是通过反射机制获取被检查方法上的注解信息,然后根据注解元素的值进行特定的处理.在上述程序中加入:
public static void track(List<Integer> list, Class<?> cl) { for (Method m : cl.getDeclaredMethods()) { AnnotateDemo ant = m.getAnnotation(AnnotateDemo.class); if (ant != null) { System.out.println("Found:" + ant.i() + "_" + ant.s()); list.remove(new Integer(ant.i())); } } for (int value : list) { System.out.println("notFound:" + value); } } public static void main(String[] args) { List<Integer> list = new ArrayList<>(); Collections.addAll(list, 97, 98, 99, 100); track(list, AnnotateDemo3.class); }
静态方法track需要List和Class的参数,遍历cl中的方法,通过getAnnotation()方法,判断cl中的方法是否被标记.如果是,则输出注解的详细信息,然后在list中remove掉对应的值,最后再输出list中剩下的值.输出结果:
Found:97_hello
Found:98_world
notFound:99
notFound:100
这也是一个简单的跟踪项目中的例子.
?
SE1.8引入了重复注解的特性,允许在声明同一类型时使用多次同一个注解,提高了程序的可读性,例:
import java.lang.annotation.Repeatable; @Repeatable(AnnotateDemoFactory.class) @interface AnnotateDemo { String s(); } @interface AnnotateDemoFactory { AnnotateDemo[] value(); } public class AnnotateDemo4 { @AnnotateDemo(s = "hello") @AnnotateDemo(s = "world") public void a() { } }
通过使用另一个注解来存储重复注解,创建重复注解AnnotateDemo时,加上@Repeatable,指向存储注解AnnotateDemoFactory.在使用时可以重复使用AnnotateDemo来进行标记.
?
小结:了解java中注解的用法需要对反射有一定了解,通过使用注解可以实现生成文档,编译检查等功能,同时增加了程序的可读性.