参考:
- 秒懂,Java 注解 (Annotation)你可以这样学
- 疯狂java讲义
1. 定义
同 classs
和 interface
一样,注解也属于一种类型。
注解通过 @interface
关键字进行定义。
public @interface TestAnnotation {
}
2. 语法
默认情况下,Annotation可用于修饰任何程序元素,包括接口、类、方法等。
Annotation成员变量
以方法形式定义
Annotation中的成员变量以无形参的方法形式来声明。定义了几个成员变量,在使用时必须给值。
public @interface MyTag {
String name();
int age();
}
有个默认值,在使用时可以不给值。
public @interface MyTag {
String name() default "hobe";
int age() default 18;
}
元Annotation
jdk除了java.lang下的5个基本Annotation:
- @Override(限定重写)
- @Deprecated(标记过时)
- @SuppressWarnings(抑制警告)
- @SafeVarargs(java7)
- @functionalInterface(java8)
之外,java.lang.annotation
包下提供了6个Meta Annotation
(元Annotation),其中5个都用于修饰其他Annotation
。主要几个如下:
1 @Retention
只能修饰Annotation定义,指定修饰多长时间,其源码:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
其中只有一个成员变量。
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
SOURCE
:用于检查代码,编译时丢掉。(主要看IDE是否报错)CLASS
:(默认)。编译后也会记录在class文件中。运行时,JVM不可获取Annotation信息,不可反射获取。RUNTIME
:(通常会使用)。编译后也会记录在class文件中。运行时,JVM可获取Annotation信息,可反射获取。
使用示例:
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTag {
...
}
或:
@Retention(value = RetentionPolicy.RUNTIME)
public @interface MyTag {
...
}
说明:
当Annotation成员变量名为value
时,只需为value指定值时, 可以在括号里直接写出value的值,无需name=value
的形式。
2 @Target
只能修饰Annotation定义。指定哪些程序单元可以使用,源码:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Formal parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE
}
如只能修饰成员变量则使用:
@Target({ElementType.FIELD})
3 @Documented
被该Annotation修饰的类将会被javadoc工具提取成文档。
4 @Inherited
被@Inherited修饰的注解,用于父类时,子类自动会加该注解。
System.out.println(ChildClass.class.isAnnotationPresent(MyTag.class));
为true
。
3. 自定义Annotation
分类:标记Annotation
和元数据Annotation
根据Annotation是否包含成员变量,将其分为两类:
- 标记Annotation:没有定义成员变量,尽利用自身是否存在与否来提供信息。如@Test,@Override
- 元数据Annotation:包含成员变量。
注意的一种情况是一个注解没有任何属性。比如
public @interface Perform {}
那么在应用这个注解的时候,括号都可以省略。
@Perform
public void testMethod(){}
示例:
@Target({ElementType.FIELD})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface MyTag {
String name() default "hobe"; //字符串
int age() default 18; //int
String[] likes(); // 数组
Sex sex(); //枚举
}
反射提取Annotation信息
使用Annotation修饰了类、方法、成员变量等成员之后,这些Annotation并不会自己生效。必须由开发者提取信息并处理。
java.lang.reflect增加了读取运行时Annotation的能力。如:
- getAnnotation()
- getAnnotations()
- isAnnotationPresent()
- getAnnotationsByType()
- ...
如获取Mytag注解中info方法上的所有注解,则:
Class.forName("MyTag").getMethods("info").getAnnotations()
使用示例
- 注解类:
@Target({ElementType.FIELD,ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface MyTag {
String name() default "hobe"; //字符串
int age() default 18; //int
String[] likes(); // 数组
Sex sex() default Sex.BOY; //枚举
}
public enum Sex {
BOY,GIRL
}
- 工具类(这里工具类和被注解类放在一起了)
@MyTag(likes = {"code","ball"})
public class Demo {
private String name;
private Integer age;
private String[] likes;
private Sex sex;
public static void main(String[] args) {
Demo demo = new Demo();
/** 仅仅注解,并不能将值赋给Demo的字段 */
System.out.println(demo);
boolean hasAnnotation = Demo.class.isAnnotationPresent(MyTag.class);
if (hasAnnotation){
MyTag myTag = Demo.class.getAnnotation(MyTag.class);
System.out.println(myTag.name());
System.out.println(myTag.likes());
System.out.println(myTag.sex());
System.out.println(myTag.age());
}
}
...
}
结果:
Demo{name='null', age=null, likes=null, sex=null}
hobe
[Ljava.lang.String;@4617c264
BOY
18
参考:
- 秒懂,Java 注解 (Annotation)你可以这样学
- 疯狂java讲义