1. 簡介
注解(也被稱為中繼資料),為我們在代碼中添加資訊提供了一種形式化的方法。注解在一定程度上是把中繼資料與源代碼檔案結合在一起,而不是儲存在外部文檔中這一大趨勢之下所催生的。
它可以提供用來完整的描述程式所需的資訊,而這些資訊是無法使用java來表達的。是以,注解使得我們能夠以将編譯器來測試和驗證的格式,存儲有關程式的額外資訊。注解可以用來生成描述符檔案,甚至是新的類定義。通過使用注解,我們可以将這些中繼資料儲存在java源代碼中,并利用annotation
api為自己的注解構造處理工具。
注解可以生成更加幹淨易讀的代碼以及編譯器類型檢查等等。
注解(annotation)實在實際的源代碼級别儲存所有的資訊,而不是某種注釋性文字(comment),這使得代碼更加簡潔,便于維護。
2. 注解分類
按照運作機制分類
描述
源碼注解
注解隻在源碼中存在,編譯成.class檔案就不存在了
編譯時注解
注解隻在源碼和.class檔案中都存在(例如:@override)
運作時注解
在運作階段還起作用,甚至影響運作邏輯的注解(例如:@autowired)
3. 内置注解:
(1)@override
表示目前的方法定義将覆寫超類中的方法。如果你不小心拼寫錯誤,或者方法簽名對不上被覆寫的方法,編譯器就會發出錯誤提示。
(2)@deprecated
如果程式員使用了注解為它的元素,那麼編譯器會發出警告資訊。
(3)@suppresswarnings
關閉不當的編譯器警告資訊(在java se5 之前,也可以使用這個注解,不過被忽略不起作用)
4. 基本文法
4.1 定義注解
可以看到注解的定義很像接口的定義。事實上,與其他任何java接口一樣,注解也會被編譯成class檔案。
<code>package com.qunar.annotation;</code>
<code> </code>
<code>import java.lang.annotation.documented;</code>
<code>import java.lang.annotation.elementtype;</code>
<code>import java.lang.annotation.inherited;</code>
<code>import java.lang.annotation.retention;</code>
<code>import java.lang.annotation.retentionpolicy;</code>
<code>import java.lang.annotation.target;</code>
<code>public class annotation {</code>
<code>// 定義description注解</code>
<code>@target(elementtype.method)</code>
<code>@retention(retentionpolicy.runtime)</code>
<code>@inherited</code>
<code>@documented</code>
<code>// 使用@interface 關鍵字定義注解</code>
<code>public @interface description{</code>
<code>// 成員以無參無異常方式聲明</code>
<code>string desc();</code>
<code>string author();</code>
<code>// 可以使用default關鍵字為成員指定一個預設值</code>
<code>int age() default 18;</code>
<code>}</code>
除了@符号以外,@description的定義很像一個接口。定義注解的時候會需要一些元注解,如@target和@retention。@target用來定義你的注解将用于什麼地方(是一個方法上還是一個類上),@retention用來定義該注解在哪一個級别上可用(在源代碼上或者是類檔案上或者是運作時),具體下面講解。
4.2 注解元素
注解@description中包含int元素age,以及string元素desc和author。注解元素可以使用的類型如下:
所有基本資料類型(int,float,boolean等)
string
class
enum
annotation
以上類型的數組
如果你使用了其他類型,那麼編譯器就會報錯。注意,也不允許使用任何包裝類型,不過由于自動打包的存在,這算不上什麼限制。注解也可以作為元素的類型,也就是注解可以嵌套。
4.3 預設值限制
編譯器對元素的預設值有些過分的挑剔。
首先,元素不能有不确定的值,也就是說元素必須要麼有預設值,要麼使用注解時提供元素的值。
其次,對于非基本類型的元素,無論是在源代碼中聲明時,或者是在注解接口中定義預設值時,都不能以null作為其值。為了這個限制,我們隻能自己定義一些特殊的值,例如空字元串或者負數,來表示某個元素不存在。
4.4 元注解
元注解隻負責注解其他的注解。
元注解
參數
@taget
constructor
構造器的聲明
表示注解可以用于什麼地方
field
域聲明
method
方法聲明
package
包聲明
parameter
參數聲明
type
類,接口或enum聲明
local_variable
局部變量聲明
@retention
source
表示需要在什麼級别儲存該注解資訊
注解隻會在.class檔案存在,會被vm丢棄
runtime
vm将在運作期也保留注解,是以可以通過反射機制讀取注解的資訊
@document
将此注解包含在javadoc中
@inherited
允許子類繼承父類中的注解
4.5 使用注解
文法:@<注解名稱>(<成員名1> = <成員值1>,<成員名2> = <成員值2>,...)
<code>import com.qunar.annotation.annotation.description;</code>
<code>public class student {</code>
<code>private string name;</code>
<code></code>
<code>@description(desc = "set name for student object" , author = "sjf0115")</code>
<code>public string getname() {</code>
<code>return name;</code>
<code>@description(desc = "get name from student object" , author = "sjf0115", time = "2016-01-11")</code>
<code>public void setname(string name) {</code>
<code>this.name = name;</code>
5. 解析注解
通過反射機制擷取類,函數或者成員上的運作時注解資訊,進而實作動态控制程式運作的邏輯。
<code>import java.lang.reflect.method;</code>
<code>public class parseannotation {</code>
<code>public static void main(string[] args){</code>
<code>class<?> class1 = null;</code>
<code>try {</code>
<code>// 使用類加載器加載類</code>
<code>class1 = class.forname("com.qunar.annotation.student");</code>
<code>} catch (classnotfoundexception e) {</code>
<code>e.printstacktrace();</code>
<code>// 判斷student類上是否有description注解</code>
<code>boolean isexits = class1.isannotationpresent(description.class);</code>
<code>if(isexits){</code>
<code>// 注解執行個體</code>
<code>description desc = class1.getannotation(description.class);</code>
<code>system.out.println("注解:" + desc.tostring());</code>
<code>}//if</code>
<code>// 擷取student類上的所有方法</code>
<code>method[] methods = class1.getmethods();</code>
<code>// 周遊所有方法</code>
<code>for (method method : methods) {</code>
<code>// 判斷方法上是否有description注解</code>
<code>isexits = method.isannotationpresent(description.class);</code>
<code>description description = method.getannotation(description.class);</code>
<code>system.out.println("方法注解:" + description.tostring());</code>
<code>}//for</code>
運作結果:
方法注解:@com.qunar.annotation.annotation$description(time=2016-01-12, desc=set name for student object, author=sjf0115)
方法注解:@com.qunar.annotation.annotation$description(time=2016-01-11, desc=get name from student object, author=sjf0115)
<code>import java.lang.annotation.annotation;</code>
<code>// 方法上擷取所有的注解</code>
<code>annotation[] annotations = method.getannotations();</code>
<code>for (annotation annotation : annotations) {</code>
<code>if(annotation instanceof description){</code>
<code>system.out.println("description注解:" + annotation.tostring());</code>
這兩個程式都用到了反射的方法:getmethods()和getannotation(),它們都屬于annotatedelement接口(class,method與field等類都實作了該接口)。getannotation()方法傳回指定類型的注解對象,在這裡就是description。如果被注解的方法上沒有該類型的注解,則傳回null值。