本博文是對Java中註解相關知識點的簡單總結,如有敘述不清晰或是不許確的地方,但願你們能夠指正,謝謝你們:)java
咱們你們都知道Java代碼中使用註釋是爲了向之後閱讀這份代碼的人解釋說明一些事情,註解是註釋的升級版,它能夠向編譯器、虛擬機等解釋說明一些事情。好比咱們很是熟悉的@Override就是一種元註解,它的做用是告訴編譯器它所註解的方法是重寫父類的方法,這樣編譯器就會去檢查父類是否存在這個方法,以及這個方法的簽名與父類是否相同。git
也就是說,註解是描述Java代碼的代碼,它可以被編譯器解析,註解處理工具在運行時也可以解析註解。咱們在Java源文件中使用註釋,是爲了之後咱們或他人再來讀這段代碼時,可以更好地理解它。Javadoc工具能夠解析咱們在源代碼中爲類、方法、變量等添加的描述信息,並根據這些描述信息自動生成一個HTML文檔,這些自動生成的文檔便可做爲API幫助文檔。只要咱們爲類、方法等添加的描述信息符合Javadoc要求的語法,咱們就可以使用Javadoc工具根據咱們的描述信息自動生成一個幫助文檔。而註解比java註釋和Javadoc要強大得多,它們三者之間的重大的區別在於,Java註釋和Javadoc描述所發揮的做用僅僅到編譯時就止步了,而註解直到運行時都可以發揮做用。數組
咱們知道,使用「transient」關鍵字能夠告訴編譯器這個域不可序列化。相比於用」transient「這樣的關鍵字修飾一個屬性,註解爲咱們提供了爲類/方法/屬性/變量添加描述信息的更通用的方式,而這些描述信息對於開發者、自動化工具、Java編譯器和Java運行時來講都是有意義的,也就是說他們都能「讀懂」註解信息。」transient「關鍵字是一個修飾符,而註解也是一種修飾符。除了傳遞信息,咱們也可使用註解生成代碼。咱們可使用註解,而後讓註解解析工具來解析它們,以此來生成一些」模板化「的代碼。好比Hibernate、Spring、Axis這些框架大量使用了註解,來避免一些重複的工做。框架
元註解即用來描述註解的註解,好比如下代碼中咱們使用「@Target」元註解來講明MethodInfo這個註解只能應用於對方法進行註解:ide
@Target(ElementType.METHOD)
public @interface MethodInfo { ... }
下面咱們來具體介紹一下幾種元註解。函數
當一個註解類型被@Documented元註解所描述時,那麼不管在哪裏使用這個註解,都會被Javadoc工具文檔化。咱們來看一下它的定義:工具
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Documented { }
咱們從以上代碼中能夠看到,定義註解使用@interface關鍵字,這就比如咱們定義類時使用class關鍵字,定義接口時使用interface關鍵字同樣,註解也是一種類型。這個元註解被@Documented修飾,表示它自己也會被文檔化。@Retention元註解的值RetentionPolicy.RUNTIME表示@Documented這個註解能保留到運行時;@Target元註解的值ElementType.ANNOTATION_TYPE表示@Documented這個註解只可以用來描述註解類型。spa
代表被修飾的註解類型是自動繼承的。具體解釋以下:若一個註解類型被Inherited元註解所修飾,則當用戶在一個類聲明中查詢該註解類型時,若發現這個類聲明中不包含這個註解類型,則會自動在這個類的父類中查詢相應的註解類型,這個過程會被重複,直到該註解類型被找到或是查找完了Object類還未找到。這個元註解的定義以下:code
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Inherited { }
咱們能夠看到這個元註解類型被@Documented所註解,可以保留到運行時,只能用來描述註解類型。blog
咱們在上面已經見到個這個元註解,它表示一個註解類型會被保留到何時,好比如下代碼表示Developer註解會被保留到運行時:
@Retention(RetentionPolicy.RUNTIME)
public @interface Developer { String value(); }
@Retention元註解的定義以下:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { RetentionPolicy value(); }
咱們在使用@Retention時,後面括號裏的內容即表示他的取值,從以上定義咱們能夠看到,取值的類型爲RetentionPolicy,這是一個枚舉類型,它能夠取如下值:
這個元註解說明了被修飾的註解的應用範圍,也就是被修飾的註解能夠用來註解哪些程序元素,它的定義以下:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { ElementType[] value(); }
從以上定義咱們能夠看到它也會保留到運行時,並且它的取值是爲ElementType[]類型(一個數組,意思是能夠指定多個值),ElementType是一個枚舉類型,它能夠取如下值:
Java自己內建了一些註解,下面咱們來介紹一下咱們在平常開發中比較常見的註解:@Override、@Deprecated、@SuppressWarnings。相信咱們你們或多或少都使用過這三個註解,下面咱們一塊兒再從新認識一下它們。
咱們先來看一下這個註解類型的定義:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
從它的定義咱們能夠看到,這個註解能夠被用來修飾方法,而且它只在編譯時有效,在編譯後的class文件中便再也不存在。這個註解的做用咱們你們都不陌生,那就是告訴編譯器被修飾的方法是重寫的父類的中的相同簽名的方法,編譯器會對此作出檢查,若發現父類中不存在這個方法或是存在的方法簽名不一樣,則會報錯。
這個註解的定義以下:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE}) public @interface Deprecated { }
從它的定義咱們能夠知道,它會被文檔化,可以保留到運行時,可以修飾構造方法、屬性、局部變量、方法、包、參數、類型。這個註解的做用是告訴編譯器被修飾的程序元素已被「廢棄」,再也不建議用戶使用。
這個註解咱們也比較經常使用到,先來看下它的定義:
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.SOURCE) public @interface SuppressWarnings { String[] value(); }
它可以修飾的程序元素包括類型、屬性、方法、參數、構造器、局部變量,只能存活在源碼時,取值爲String[]。它的做用是告訴編譯器忽略指定的警告信息,它能夠取的值以下所示:
這個註解的使用示例以下:
@SuppressWarning(value={"deprecation", "unchecked"}) public void myMethos() {...}
經過使用以上註解,咱們告訴編譯器忽略myMethod方法中因爲使用了廢棄的類或方法或是作了未檢查的轉換而產生的警告。
咱們能夠建立咱們本身的註解類型並使用它。請看下面的示例:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Inherited public @interface MethodInfo { String author() default "absfree"; String date(); int version() default 1; }
在自定義註解時,有如下幾點須要咱們瞭解:
咱們再把上面提到過的@SuppressWarnings這個註解類型的定義拿出來看一下,這個註解類型是系統爲咱們定義好的,它的定義以下:
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.SOURCE) public @interface SuppressWarnings { String[] value(); }
咱們能夠看到,它只定義了一個註解方法value(),它的返回值類型爲String[],沒有指定默認返回值。咱們使用@SuppressWarnings這個註解所用的語法以下:
@SuppressWarnings(value={"value1", "value2", ...})
也就是在註解類型名稱後的括號內爲每一個註解方法指定返回值就可使用這個註解。下面咱們來看看怎麼使用咱們自定義的註解類型@MethodInfo:
public class AnnotationTest { @MethodInfo(author="absfree", date="20160410") public static void main(String[] args) { System.out.println("Using custom annotation..."); } }
那麼如今問題來了,咱們使用的自定義註解對於編譯器或是虛擬機來講是有意義的嗎(編譯器或是虛擬機能讀懂嗎)?顯然咱們什麼都不作的話,編譯器或者虛擬機是讀不懂咱們的自定義註解的。下面咱們來介紹如下註解的解析,讓編譯器或虛擬機可以讀懂咱們的自定義註解。
編譯時註解指的是@Retention的值爲CLASS的註解,對於這類註解的解析,咱們只需作如下兩件事:
實際上,編譯器在編譯時會自動查找全部繼承自 AbstractProcessor 的類,而後調用他們的 process 方法。所以咱們只要作好上面兩件事,編譯器就會主動去解析咱們的編譯時註解。如今,咱們把上面定義的MethodInfo的Retention改成CLASS,咱們就能夠按照如下代碼來解析它:
@SupportedAnnotationTypes({ "com.custom.customannotation.MethodInfo" }) public class MethodInfoProcessor extends AbstractProcessor { @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) { HashMap<String, String> map = new HashMap<String, String>(); for (TypeElement typeElement : annotations) { for (Element element : env.getElementsAnnotatedWith(typeElement)) { MethodInfo methodInfo = element.getAnnotation(MethodInfo.class); map.put(element.getEnclosingElement().toString(), methodInfo.author()); } } return false; } }
@SupportedAnnotationTypes註解描述了Processor要解析的註解的名字。process 函數的annotations參數表示 表示待處理的註解集,env表示當前或是以前的運行環境。process函數的返回值表示annotations中的註解是否被這個Processor接受。
首先咱們把MethodInfo註解類型中Retention的值改回原來的RUNTIME,接下來咱們介紹如何經過反射機制在運行時解析咱們的自定義註解類型。
java.lang.reflect包中有一個AnnotatedElement接口,這個接口定義了用於獲取註解信息的幾個方法:
T getAnnotation(Class annotationClass) //返回該程序元素的指定類型的註解,若不存在這個類型的註解則返回null Annotation[] getAnnotations() //返回修飾該程序元素的全部註解 Annotation[] getDeclaredAnnotations() //返回直接修飾該元素的全部註解 boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) //當該程序元素被指定類型註解修飾時,返回true,不然返回false
解析咱們上面的自定義註解MethodInfo的相關示例代碼以下(AnnotationParser.java):
public class AnnotationParser { public static void main(String[] args) { try { Class cls = AnnotationTest.class; for (Method method : cls.getMethods()) { MethodInfo methodInfo = method.getAnnotation(MethodInfo.class); if (methodInfo != null) { System.out.println("method name:" + method.getName()); System.out.println("method author:" + methodInfo.author()); System.out.println("method date:" + methodInfo.date()); System.out.println("method version:" + methodInfo.version()); } } } catch (Exception e) { e.printStackTrace(); } } }
運行以上代碼咱們能夠獲得如下輸出:
![](http://static.javashuo.com/static/loading.gif)
這說明咱們已經成功解析了自定義註解。關於註解有點咱們須要明確的是,做爲描述代碼自己的一種元數據,註解是一種」被動「的信息。也就是說,必須由編譯器或虛擬機來「主動」解析它,它才能發揮本身的做用。
1. Java Documention
2. 公共技術點之Java註解
3. Java 註解