NO IMAGE

原文地址為:深入理解Java:註解(Annotation)基本概念

什麼是註解(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 class Fruit {

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("水果的名字是:蘋果");
}
}
  Orange 類編譯不會有任何問題,Apple 類在編譯的時候會提示相應的錯誤。@Override註解只能用於方法,不能用於其他程式元素。

@Deprecated,標記已過時:

  同 樣Deprecated也是一個標記註解。當一個型別或者型別成員使用@Deprecated修飾的話,編譯器將不鼓勵使用這個被標註的程式元素。而且這種修飾具有一定的 “延續性”:如果我們在程式碼中通過繼承或者覆蓋的方式使用了這個過時的型別或者成員,雖然繼承或者覆蓋後的型別或者成員並不是被宣告為 @Deprecated,但編譯器仍然要報警。

  值得注意,@Deprecated這個annotation型別和javadoc中的 @deprecated這個tag是有區別的:前者是java編譯器識別的,而後者是被javadoc工具所識別用來生成文件(包含程式成員為什麼已經過 時、它應當如何被禁止或者替代的描述)。

  在java5.0,java編譯器仍然象其從前版本那樣尋找@deprecated這個javadoc tag,並使用它們產生警告資訊。但是這種狀況將在後續版本中改變,我們應在現在就開始使用@Deprecated來修飾過時的方法而不是 @deprecated javadoc tag。

  下面一段程式中使用了@Deprecated註解標示方法過期,同時在方法註釋中用@deprecated tag 標示該方法已經過時,程式碼如下:

 class AppleService {
public void displayName(){
System.out.println("水果的名字是:蘋果");
}

/**
* @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);
}

}

  AppleService類的showTaste() 方法被@Deprecated標註為過時方法,在FruitRun類中使用的時候,編譯器會給出該方法已過期,不推薦使用的提示。


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的成員賦值。例項如下:

public class FruitService {

@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 annotation型別只定義了一個單一的成員,所以只有一個簡單的value={…}作為name=value對。又由於成員值是一個陣列,故使用大括號來宣告陣列值。注意:我們可以在下面的情況中縮寫annotation:當annotation只有單一成員,併成員命名為”value=”。這時可以省去”value=”。比如將上面方法getFruit()的SuppressWarnings annotation就是縮寫的。

   SuppressWarnings註解的常見引數值的簡單說明:

    1.deprecation:使用了不贊成使用的類或方法時的警告;
    2.unchecked:執行了未檢查的轉換時的警告,例如當使用集合時沒有用泛型 (Generics) 來指定集合儲存的型別;
    3.fallthrough:當 Switch 程式塊直接通往下一種情況而沒有 Break 時的警告;
    4.path:在類路徑、原始檔路徑等中有不存在的路徑時的警告;
    5.serial:當在可序列化的類上缺少 serialVersionUID 定義時的警告;
    6.finally:任何 finally 子句不能正常完成時的警告;
    7.all:關於以上所有情況的警告。

 

   

 

轉載請註明本文地址:深入理解Java:註解(Annotation)基本概念