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());
}
}
}
謝謝閱讀!