java用@interface定义注解。在程序中加上注解,JAVAC编译器和VM可以利用它来做一些相应的处理。
元注解
元注解是注解的注解。有如下这一些:
@Retention
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
表明带注解类型的注解将会保留多久。如果一个注解类型的声明上没有Retention注解,那么它默认的保留策略是RetentionPolicy.CLASS。
Retention注解有一个属性value,它是用来指定保留策略的。这个属性的类型是RetentionPolicy,它的值有:
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
}
(1)SOURCE:表示注解的信息会被编译器丢弃,不会保留在class文件中,注解的信息只会留在源文件中,所以这里注解信息一般都用在编译阶段。
(2) CLASS :表示注解的信息会被编译器记录在class文件中;但在运行时,不会被虚拟机保留着,所以它们不可以用反射的方式读取到;如果不指定Retention的值,默认就是CLASS;
(3)RUNTIME:表示注解的信息会被编译器保留在class文件里;在运行时,还会被虚拟机保留着,所以它们可以用反射的方式读取到,所以这里注解信息一般都用在程序运行阶段;
例子:
@Retention(RetentionPolicy.SOURCE)
public @interface MyAnnotation1 {
}
@Retention(RetentionPolicy.CLASS)
public @interface MyAnnotation2 {
}
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation3 {
}
@Target
@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();
}
表明注解类型适用的上下文。如果@Target元注解没有在注解类型上,那么这个注解可以作为一个修饰符用在任何声明上,除了类型参数声明。如有注解类型上有@Target元注解,那么编译器将根据ElementType枚举常量限制它的使用范围。@Target有一个属性ElementType[]类型的属性 value(),就是通过它来指定的。ElementType枚举常量有以下这些:
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
}
- TYPE: 类、接口(包括注解类型)、枚举声明
- FIELD:字段声明(包括枚举常量)
- METHOD:方法声明
- PARAMETER:形式参数声明
- CONSTRUCTOR:构造函数声明
- LOCAL_VARIABLE:局部变量声明
- ANNOTATION_TYPE:注释类型声明
- PACKAGE:包声明
- TYPE_PARAMETER:类型参数声明
- TYPE_USE:类型的使用
这个@Target元注解表示声明的类型是仅用作复杂注解类型中的成员类型声明。它不能直接用于注释任何内容,如:
@Target({})
public @interface MemberType {
...
}
综合例子
@Documented
@Retention(CLASS)
@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE})
public @interface NonNull {
}
我们常自定义用@Retention(RetentionPolicy.RUNTIME) 注释的注解,因为这样的注解会保留在VM中,这样我们才有办法通过反射技术来读取到注解,再做相应处理,事不宜迟,我们来自定义一个:
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation{
String city() default "Test";
String name();
int[] array() default { 2, 4, 5, 6 };
EnumTest.TrafficLamp lamp() ;
TestAnnotation lannotation() default @TestAnnotation(value = "ddd");
Class style() default String.class;
}
上面程序中,定义一个注解@MyAnnotation,定义了6个属性,他们的名字为:
city,name,array,lamp,lannotation,style.
属性city类型为String,默认值为gege
属性name类型为String,没有默认值
属性array类型为数组,默认值为2,4,5,6
属性lamp类型为一个枚举,没有默认值
属性lannotation类型为注解,默认值为@TestAnnotation,注解里的属性是注解
属性style类型为Class,默认值为String类型的Class类型
使用注解MyAnnotation:
@MyAnnotation( city= "beijing", name="zhijincheng",array={},lamp=TrafficLamp.RED,style=int.class)
public class TestExample
{
@MyAnnotation(lannotation=@TestAnnotation(value="baby"), name = "zhijincheng",array={1,2,3},lamp=TrafficLamp.YELLOW)
@Deprecated
@SuppressWarnings("")
public void read()
{
System.out.println("output something!");
}
}
通过反射读取注解的信息:
public class MyAnnotationReflection{
public static void main(String[] args) throws Exception{
TestExample testExample = new TestExample();
// 如果TestExample类名上有注解@MyAnnotation修饰,则为true
if(TestExample.class.isAnnotationPresent(MyAnnotation.class)){
System.out.println("There is MyAnnotation");
}
Class<TestExample> c = TestExample.class;
Method method = c.getMethod("read", new Class[] {});
if (method.isAnnotationPresent(MyAnnotation.class)){
// 调用read方法
method.invoke(testExample, null);
// 获取方法上注解@MyAnnotation的信息
MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
String city = myAnnotation.city();
String name = myAnnotation.name();
// 打印属性city和name的值
System.out.println(city + ", " + name);
// 打印属性array数组的长度
System.out.println(myAnnotation.array().length);
// 打印属性lannotation的值
System.out.println(myAnnotation.lannotation().value());
System.out.println(myAnnotation.style());
}
// 得到read方法上的所有注解
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations){
System.out.println(annotation.annotationType().getName());
}
}
}
谢谢阅读!