Annotation(註解),Annotation是代碼裏的特殊標記,這些標記能夠在編譯、類加載、運行時被讀取,並執行相應的處理。
Annotation提供了一種爲程序元素(包、類、構造器、方法、成員變量、參數、局域變量)設置元數據的方法。
Annotation不能運行,它只有成員變量,沒有方法。Annotation跟public、final等修飾符的地位同樣,都是程序
元素的一部分,Annotation不能做爲一個程序元素使用。
經過使用Annotation,開發人員能夠在不改變原有邏輯的狀況下,在源文件中嵌入一些補充的信息
一、定義Annotation
使用@interface關鍵字(在原有interface關鍵字前增長@符號)。定義一個新的Annotation類型與定義一個接口很像。
定義完該Annotation後,就能夠在程序中使用該Annotation。
使用Annotation,很是相似於public、final這樣的修飾符,一般,會把Annotation另放一行,而且放在全部修飾符以前。
@Testpublic class MyClass{....}
1.1 成員變量
Annotationa只有成員變量!沒有方法,Annotation的成員變量在Annotation定義中以「無形參的方法」形式來聲明,
成員變量以方法的形式來定義
,其返回值定義了該成員變量的類型。
public @interface MyTag{ string name(); int age();}
在Annotation裏定義了成員變量後,使用該Annotation時就應該爲該Annotation的成員變量指定值
public class Test{ @MyTag(name="紅薯",age=30) public void info(){ ...... }}
也能夠在定義Annotation的成員變量時,爲其指定默認值,指定成員變量默認值使用default關鍵字。
public @interface MyTag{ string name() default "我蘭"; int age() default 18;}
Annotation的成員變量已經指定了默認值,使用該Annotation時能夠不爲這些成員變量指定值,而是直接使用默認值。
public class Test{ @MyTag public void info(){ ...... }}
根據Annotation是否包含成員變量,能夠把Annotation分爲以下兩類:
標記Annotation:沒有成員變量的Annotation被稱爲標記。這種Annotation僅用自身的存在與否來爲咱們提供信息,例如
@override等。
元數據Annotation:包含成員變量的Annotation。由於它們能夠接受更多的元數據,所以被稱爲元數據Annotation。
1.2 元註解
JDK提供的元註解來修飾Annotation定義
@Retention:用於指定Annotation能夠保留多長時間,包含一個名爲「value」的成員變量
value:
RetentionPolicy.SOURCE:Annotation只保留在源代碼中,編譯器編譯時,直接丟棄這種Annotation。
RetentionPolicy.CLASS:編譯器把Annotation記錄在class文件中。當運行Java程序時,JVM中再也不保留該Annotation。RetentionPolicy.RUNTIME:編譯器把Annotation記錄在class文件中。當運行Java程序時,JVM會保留該Annotation,
程序能夠經過反射獲取該Annotation的信息。
//直接指定@Retention(RetentionPolicy.RUNTIME)public @interface MyTag{ String name() default "我蘭";}
若是Annotation裏有一個名爲「value「的成員變量,使用該Annotation時,能夠直接使用XXX(val)形式爲value成員變量賦值,無須使用name=val形式。
好比:xUtils裏面的
@Target:指定Annotation用於修飾哪些程序元素。
ElementType.TYPE:能修飾類、接口或枚舉類型
ElementType.FIELD:能修飾成員變量
ElementType.METHOD:能修飾方法
ElementType.PARAMETER:能修飾參數
ElementType.CONSTRUCTOR:能修飾構造器
ElementType.LOCAL_VARIABLE:能修飾局部變量
ElementType.ANNOTATION_TYPE:能修飾註解
ElementType.PACKAGE:能修飾包
@Target({ ElementType.FIELD, ElementType.METHOD })public @interface AnnTest { String name() default "sunchp";}
@Documented:若是定義註解A時,使用了@Documented修飾定義,則在用javadoc命令生成API文檔後,
全部使用註解A修飾的程序元素,將會包含註解A的說明。
@Documentedpublic @interface Testable {}public class Test { @Testable public void info() { }}
@Inherited:指定Annotation具備繼承性。
1.3 基本Annotation
JDK默認提供了以下幾個基本Annotation:
@Override 限定重寫父類方法。對於子類中被@Override 修飾的方法,若是存在對應的被重寫的父類方法,則正確;若是不存在,則報錯。@Override 只能做用於方法,不能做用於其餘程序元素。
@Deprecated 用於表示某個程序元素(類、方法等)已過期。若是使用被@Deprecated修飾的類或方法等,編譯器會發出警告
@SuppressWarning 抑制編譯器警告。指示被@SuppressWarning修飾的程序元素(以及該程序元素中的全部子元素,例如類以及該類中的方法.....)取消顯示指定的編譯器警告。例如,常見的@SuppressWarning(value="unchecked") ;
@SafeVarargs @SafeVarargs是JDK 7 專門爲抑制「堆污染」警告提供的。
二、提取Annotation信息(反射)
當開發者使用了Annotation修飾了類、方法、Field等成員以後,這些Annotation不會本身生效,
必須由開發者提供相應的代碼來提取並
處理Annotation信息。
PS:若是想要在運行時提取註解信息,在定義註解時,該註解必須使用@Retention(RetentionPolicy.RUNTIME)修飾。
JDK主要提供了兩個類,來完成Annotation的提取:
java.lang.annotation.Annotation接口:這個接口是全部Annotation類型的父接口(後面會分析Annotation的本質,
Annotation本質是接口,而java.lang.annotation.Annotation接口是這些接
口的父接口)。
//java.lang.annotation.Annotation接口源碼:package java.lang.annotation;public interface Annotation { boolean equals(Object obj); int hashCode(); String toString(); Class<? extends Annotation> annotationType();}
annotationType( ),用於返回該註解的java.lang.Class。
java.lang.reflect.AnnotatedElement接口:該接口表明程序中能夠被註解的程序元素。
//java.lang.reflect.AnnotatedElement接口源碼:package java.lang.reflect;import java.lang.annotation.Annotation;public interface AnnotatedElement { boolean isAnnotationPresent(Class<? extends Annotation> annotationClass); <T extends Annotation> T getAnnotation(Class<T> annotationClass); Annotation[] getAnnotations(); Annotation[] getDeclaredAnnotations();}
因此程序經過反射獲取了某個類的AnnotatedElement對象(例如,A類
method1()方法的java.lang.reflect.Method對象)後,
就能夠調用該對象的isAnnotationPresent( )、getAnnotation( )等方法來訪問註解信息。
在編譯器編譯註解定義時,自動在class文件中,添加與成員變量同名的抽象方法,用於反射時獲取成員變量的值。經過上面的示例
能夠看出,其實Annotation十分簡單,它是對源代碼增長的一些特殊標記,這些特殊標記可經過反射獲取,當程序獲取這些特殊標
記後,程序能夠作出相應的處理。
Demo:AnnotationTest