一,什么叫注解
用一個詞就可以描述注解,那就是元數據,即一種描述數據的數據。所以,可以說注解就是源代碼的元數據。比如,下面這段代碼:
1
2
3
4
|
@Override
public
String toString() {
return
"This is String Representation of current object."
;
}
|
上面的代碼中,我重寫了toString()
方法並使用了@Override
注解。但是,即使我不使用@Override注解標記代碼,程序也能夠正常執行。那么,該注解表示什么?這么寫有什么好處嗎?事實上,
@Override告訴編譯器這個方法是一個重寫方法(描述方法的元數據),如果父類中不存在該方法,編譯器便會報錯,提示該方法沒有重寫父類中的方法。如果我不小心拼寫錯誤,例如將toString()
寫成了toStrring(){double r}
,而且我也沒有使用@Override
注解,那程序依然能編譯運行。但運行結果會和我期望的大不相同。現在我們了解了什么是注解,並且使用注解有助於閱讀程序。
簡而言之 對程序進行解釋說明,使讀者更好地閱讀程序
二,為什么要使用注解
使用Annotation之前(甚至在使用之后),XML被廣泛的應用於描述元數據。不知何時開始一些應用開發人員和架構師發現XML的維護越來越糟糕了。他們希望使用一些和代碼緊耦合的東西,而不是像XML那樣和代碼是松耦合的(在某些情況下甚至是完全分離的)代碼描述。
假如你想為應用設置很多的常量或參數,這種情況下,XML是一個很好的選擇,因為它不會同特定的代碼相連。如果你想把某個方法聲明為服務,那么使用Annotation會更好一些,因為這種情況下需要注解和方法緊密耦合起來,開發人員也必須認識到這點。
另一個很重要的因素是Annotation定義了一種標准的描述元數據的方式。在這之前,開發人員通常使用他們自己的方式定義元數據。例如,使用標記interfaces,注釋,transient關鍵字等等。每個程序員按照自己的方式定義元數據,而不像Annotation這種標准的方式。
目前,許多框架將XML和Annotation兩種方式結合使用,平衡兩者之間的利弊。
簡單來說:本來可能需要很多配置文件,需要很多邏輯才能實現的內容,就可以使用一個或者多個注解來替代,這樣就使得編程更加簡潔,代碼更加清晰。
三 注解的分類
1)按照運行機制划分:
【源碼注解→編譯時注解→運行時注解】
源碼注解:只在源碼中存在,編譯成.class文件就不存在了。
編譯時注解:在源碼和.class文件中都存在。像前面的@Override、@Deprecated、@SuppressWarnings,他們都屬於編譯時注解。
運行時注解:在運行階段還起作用,甚至會影響運行邏輯的注解。像@Autowired自動注入的這樣一種注解就屬於運行時注解,它會在程序運行的時候把你的成員變量自動的注入進來。
2)按照來源划分:
【來自JDK的注解——來自第三方的注解——自定義注解】
3)元注解:
元注解是給注解進行注解,可以理解為注解的注解就是元注解。
下面舉例說明一下:
JDK自帶的注解:
Java提供了三種內建注解。
1. @Override——當我們想要復寫父類中的方法時,我們需要使用該注解去告知編譯器我們想要復寫這個方法。這樣一來當父類中的方法移除或者發生更改時編譯器將提示錯誤信息。
2. @Deprecated——當我們希望編譯器知道某一方法不建議使用時,我們應該使用這個注解。Java在javadoc 中推薦使用該注解,我們應該提供為什么該方法不推薦使用以及替代的方法。
3. @SuppressWarnings——這個僅僅是告訴編譯器忽略特定的警告信息,例如在泛型中使用原生數據類型。它的保留策略是SOURCE(譯者注:在源文件中有效)並且被編譯器丟棄。
第三方的注解:
這個主要來自於各種框架 例如:@Service @Controller 等等
自定義注解:
編寫Annotation非常簡單,可以將Annotation的定義同接口的定義進行比較。我們來看兩個例子:一個是標准的注解@Override,另一個是用戶自定義注解@Todo。
1
2
3
4
|
@Target
(ElementType.METHOD)
@Retention
(RetentionPolicy.SOURCE)
public
@interface
Override {
}
|
對於@Override注釋你可能有些疑問,它什么都沒做,那它是如何檢查在父類中有一個同名的函數呢。當然,不要驚訝,我是逗你玩的。@Override注解的定義不僅僅只有這么一點代碼。這部分內容很重要,我不得不再次重復:Annotations僅僅是元數據,和業務邏輯無關。理解起來有點困難,但就是這樣。如果Annotations不包含業務邏輯,那么必須有人來實現這些邏輯。元數據的用戶來做這個事情。Annotations僅僅提供它定義的屬性(類/方法/包/域)的信息。Annotations的用戶(同樣是一些代碼)來讀取這些信息並實現必要的邏輯。
當我們使用Java的標注Annotations(例如@Override)時,JVM就是一個用戶,它在字節碼層面工作。到這里,應用開發人員還不能控制也不能使用自定義的注解。因此,我們講解一下如何編寫自定義的Annotations。
我們來逐個講述編寫自定義Annotations的要點。上面的例子中,你看到一些注解應用在注解上。
J2SE5.0版本在 java.lang.annotation提供了四種元注解,專門注解其他的注解:
@Documented –注解是否將包含在JavaDoc中
@Retention –什么時候使用該注解
@Target? –注解用於什么地方
@Inherited – 是否允許子類繼承該注解
@Documented–一個簡單的Annotations標記注解,表示是否將注解信息添加在java文檔中。
@Retention– 定義該注解的生命周期。
RetentionPolicy.SOURCE – 在編譯階段丟棄。這些注解在編譯結束之后就不再有任何意義,所以它們不會寫入字節碼。@Override, @SuppressWarnings都屬於這類注解。
RetentionPolicy.CLASS – 在類加載的時候丟棄。在字節碼文件的處理中有用。注解默認使用這種方式。
RetentionPolicy.RUNTIME– 始終不會丟棄,運行期也保留該注解,因此可以使用反射機制讀取該注解的信息。我們自定義的注解通常使用這種方式。
@Target – 表示該注解用於什么地方。如果不明確指出,該注解可以放在任何地方。以下是一些可用的參數。需要說明的是:屬性的注解是兼容的,如果你想給7個屬性都添加注解,僅僅排除一個屬性,那么你需要在定義target包含所有的屬性。
ElementType.TYPE:用於描述類、接口或enum聲明
ElementType.FIELD:用於描述實例變量
ElementType.METHOD
ElementType.PARAMETER
ElementType.CONSTRUCTOR
ElementType.LOCAL_VARIABLE
ElementType.ANNOTATION_TYPE 另一個注釋
ElementType.PACKAGE 用於記錄java文件的package信息
@Inherited – 定義該注釋和子類的關系
例子:使用自定義注解CheckScore