什麼是註解(Annotation):
Annotation(註解)就是Java提供了一個元程式中的元素關聯任何資訊和著任何元資料(metadata)的途徑和方法。 Annotion(註解)是一個接口,程式可以透過反射來取得指定程式元素的Annotion對象,然後透過Annotion物件來取得註解裡面的元資料。
Annotation(註解)是JDK5.0及以後版本引入的。它可以用於創建文檔,追蹤程式碼中的依賴性,甚至執行基本編譯時檢查。從某些方面來看,annotation就像修飾符一樣被使用,並應用於套件、類型、構造方法、方法、成員變數、參數、本地變數的宣告。這些資訊被儲存在Annotation的「name=value」結構對中。
Annotation的成員在Annotation類型中以無參數的方法的形式被宣告。其方法名和回傳值定義了該成員的名字和類型。在此有一個特定的預設語法:允許聲明任何Annotation成員的預設值:一個Annotation可以將name=value對作為沒有定義預設值的Annotation成員的值,當然也可以使用name=value對來覆蓋其它成員默認值。這一點有些近似類別的繼承特性,父類別的建構子可以作為子類別的預設建構函數,但是也可以被子類別覆蓋。
Annotation能被用來為某個程式元素(類別、方法、成員變數等)關聯任何的資訊。要注意的是,這裡存在著一個基本的規則:Annotation不能影響程式碼的執行,無論增加、刪除Annotation,程式碼都一致的執行。另外,儘管一些annotation透過java的反射api方法在運行時被訪問,而java語言解釋器在工作時忽略了這些annotation。正是由於java虛擬機忽略了Annotation,導致了annotation類型在程式碼中是「不起作用」的; 只有透過某種配套的工具才會對annotation類型中的資訊進行存取和處理。本文將涵蓋標準的Annotation和meta-annotation類型,陪伴這些annotation類型的工具就是java編譯器(當然要以某種特殊的方式處理它們)。
-------------------------------------------------- ------------------------------
什麼是metadata(元資料):
元資料從metadata一詞譯來,就是「關於資料的資料」的意思。
元資料的功能作用很多,例如:你可能用過Javadoc的註解自動產生文件。這就是元資料功能的一種。總的來說,元資料可以用來建立文檔,追蹤程式碼的依賴性,執行編譯時格式檢查,取代現有的設定檔。如果要對於元資料的作用進行分類,目前還沒有明確的定義,不過我們可以根據它所起的作用,大致可分為三類:
1. 寫文件:透過程式碼裡標識的元資料產生文件2. 程式碼分析:透過程式碼裡標識的元資料對程式碼進行分析3. 編譯檢查:透過程式碼裡標識的元資料讓編譯器能實現基本的編譯檢查在Java中元資料以標籤的形式存在於Java程式碼中,元資料標籤的存在並不影響程式碼的編譯和執行,它只是被用來產生其它的檔案或針在執行時知道被執行程式碼的描述資訊。
綜上所述:
第一,元資料以標籤的形式存在於Java程式碼中。
第二,元資料所描述的資訊是型別安全的,即元資料內部的欄位都是有明確類型的。
第三,元資料需要編譯器以外的工具額外的處理用來產生其它的程式部件。
第四,元資料可以只存在於Java原始碼級別,也可以存在於編譯之後的Class檔案內部。
-------------------------------------------------- ------------------------------
Annotation和Annotation類型:
Annotation:
Annotation使用了在java5.0所帶來的新語法,它的行為十分類似public、final這樣的修飾符。每個Annotation具有一個名字和成員個數>=0。每個Annotation的成員具有被稱為name=value對的名字和值(就像javabean一樣),name=value裝載了Annotation的資訊。
Annotation類型:
Annotation類型定義了Annotation的名字、類型、成員預設值。一個Annotation類型可以說是一個特殊的java接口,它的成員變數是受限的,而宣告Annotation類型時需要使用新語法。當我們透過java反射api來存取Annotation時,返回值將是一個實現了該annotation類型介面的對象,透過存取這個對像我們能方便的存取到其Annotation成員。後面的章節將提到在java5.0的java.lang包裡包含的3個標準Annotation類型。
-------------------------------------------------- ------------------------------
註解的分類:
根據註解參數的個數,我們可以將註解分為三類:
1.標記註解:一個沒有成員定義的Annotation類型稱為標記註解。這種Annotation類型僅使用自身的存在與否來為我們提供資訊。例如後面的系統註解@Override;
2.單值註解3.完整註解
根據註解使用方法和用途,我們可以將Annotation分為三類:
1.JDK內建系統註解2.元註解3.自訂註解
-------------------------------------------------- ------------------------------
系統內建標準註解:
註解的語法比較簡單,除了@符號的使用外,他基本上與Java固有的語法一致,JavaSE中內建三個標準註解,定義在java.lang中:
@Override:用於修飾此方法覆寫了父類別的方法;
@Deprecated:用於修飾已經過時的方法;
@SuppressWarnnings:用來通知java編譯器禁止特定的編譯警告。
下面我們依序來看看三個內建標準註解的作用和使用場景。
-------------------------------------------------- ------------------------------
@Override,限定重寫父類別方法:
@Override 是一個標記註解類型,它被用作標註方法。它說明了被標註的方法重載了父類別的方法,起到了斷言的作用。如果我們使用了這種Annotation在一個沒有覆蓋父類別方法的方法時,java編譯器會以一個編譯錯誤來警告。這個annotaton常常在我們試圖覆蓋父類別方法而確又寫錯了方法名時發揮威力。使用方法極為簡單:使用此annotation時只要在被修飾的方法前面加上@Override即可。下面的程式碼是使用@Override修飾一個企圖重載父類別的displayName()方法,而又有拼字錯誤的實例:
public void displayName(){
System.out.println("水果的名字是:*****");
}
}
class Orange extends Fruit {
@Override
public void displayName(){
System.out.println("水果的名字是:桔子");
}
}
class Apple extends Fruit {
@Override
public void displayname(){
System.out.println("水果的名字是:蘋果");
}
}
@Deprecated,標記已過時:
同樣Deprecated也是一個標記註解。當一個類型或類型成員使用@Deprecated修飾的話,編譯器將不鼓勵使用這個被標註的程式元素。而且這種修飾具有一定的「延續性」:如果我們在程式碼中透過繼承或覆蓋的方式使用了這個過時的類型或成員,雖然繼承或覆蓋後的類型或成員並不是被聲明為@Deprecated,但編譯器仍然要警報。
值得注意,@Deprecated這個annotation類型和javadoc中的@deprecated這個tag是有區別的:前者是java編譯器識別的,而後者是被javadoc工具所識別用來產生文件(包含程式成員為什麼已經過時、它應如何被禁止或替代的描述)。
在java5.0,java編譯器仍然像其從前版本那樣尋找@deprecated這個javadoc tag,並使用它們產生警告訊息。但是這種狀況將在後續版本中改變,我們應在現在就開始使用@Deprecated來修飾過時的方法而不是@deprecated javadoc tag。
下面一段程式中使用了@Deprecated註解標示方法過期,同時在方法註解中以@deprecated tag 標示該方法已經過時,程式碼如下:
/**
* @deprecated 此方法已過期,不建議使用
*/
@Deprecated
public void showTaste(){
System.out.println("水果的蘋果的口感是:脆甜");
}
public void showTaste(int typeId){
if(typeId==1){
System.out.println("水果的蘋果的口感是:酸澀");
}
else if(typeId==2){
System.out.println("水果的蘋果的口感是:綿甜");
}
else{
System.out.println("水果的蘋果的口感是:脆甜");
}
}
}
public class FruitRun {
/**
* @param args
*/
public static void main(String[] args) {
Apple apple=new Apple();
apple.displayName();
AppleService appleService=new AppleService();
appleService.showTaste();
appleService.showTaste(0);
appleService.showTaste(2);
}
}
-------------------------------------------------- ------------------------------
SuppressWarnnings,抑制編譯器警告:
@SuppressWarnings 被用於選擇性的關閉編譯器對類別、方法、成員變數、變數初始化的警告。在java5.0,sun提供的javac編譯器為我們提供了-Xlint選項來使編譯器對合法的程式碼提出警告,此種警告從某種程度上代表了程式錯誤。例如當我們使用一個generic collection類別而又沒有提供它的型別時,編譯器會提示出"unchecked warning"的警告。通常當這種情況發生時,我們需要尋找引起警告的代碼。如果它真的表示錯誤,我們就需要修正它。例如如果警告訊息顯示我們程式碼中的switch語句沒有覆寫所有可能的case,那麼我們就應該增加一個預設的case來避免這個警告。
有時我們無法避免這種警告,例如,當我們使用必須和非generic的舊程式碼互動的generic collection類別時,我們不能避免這個unchecked warning。此時@SuppressWarning就要派上用場了,在呼叫的方法前增加@SuppressWarnings修飾,告訴編譯器停止對此方法的警告。
SuppressWarning不是一個標記註解。它有一個類型為String[]的成員,這個成員的值為被禁止的警告名稱。對於javac編譯器來講,被-Xlint選項有效的警告名也同樣對@SuppressWarings有效,同時編譯器忽略掉無法辨識的警告名。
annotation語法允許在annotation名稱後面跟著括號,括號中是使用逗號分割的name=value對用於為annotation的成員賦值。實例如下:
@SuppressWarnings(value={ "rawtypes", "unchecked" })
public static List<Fruit> getFruitList(){
List<Fruit> fruitList=new ArrayList();
return fruitList;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public static List<Fruit> getFruit(){
List<Fruit> fruitList=new ArrayList();
return fruitList;
}
@SuppressWarnings("unused")
public static void main(String[] args){
List<String> strList=new ArrayList<String>();
}
}
SuppressWarnings註解的常見參數值的簡單說明:
1.deprecation:使用了不贊成使用的類別或方法時的警告;
2.unchecked:執行了未檢查的轉換時的警告,例如當使用集合時沒有用泛型(Generics) 來指定集合保存的類型;
3.fallthrough:當Switch 程式區塊直接通往下一個情況而沒有Break 時的警告;
4.path:在類別路徑、來源檔案路徑等中有不存在的路徑時的警告;
5.serial:當可序列化的類別上缺少serialVersionUID 定義時的警告;
6.finally:任何finally 子句不能正常完成時的警告;
7.all:關於以上所有情況的警告。