天天看點

[Java開發之路](15)注解

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 使用注解

文法:@&lt;注解名稱&gt;(&lt;成員名1&gt; = &lt;成員值1&gt;,&lt;成員名2&gt; = &lt;成員值2&gt;,...)

<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&lt;?&gt; 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值。