java 註解

什麼是註解(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使用了在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()方法,而又存在拼寫錯誤的實例:

[java] view plain copy
  1. public class Fruit {  
  2.   
  3.     public void displayName(){  
  4.         System.out.println("水果的名字是:*****");  
  5.     }  
  6. }  
  7.   
  8. class Orange extends Fruit {  
  9.     @Override  
  10.     public void displayName(){  
  11.         System.out.println("水果的名字是:桔子");  
  12.     }  
  13. }  
  14.   
  15. class Apple extends Fruit {  
  16.     @Override  
  17.     public void displayname(){  
  18.         System.out.println("水果的名字是:蘋果");  
  19.     }  
  20. }  

 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 標示該方法已經過時,代碼如下:

[java] view plain copy
  1. class AppleService {  
  2.     public void displayName(){  
  3.         System.out.println("水果的名字是:蘋果");  
  4.     }  
  5.       
  6.     /** 
  7.      * @deprecated 該方法已經過期,不推薦使用 
  8.      */  
  9.     @Deprecated  
  10.     public void showTaste(){  
  11.         System.out.println("水果的蘋果的口感是:脆甜");  
  12.     }  
  13.       
  14.     public void showTaste(int typeId){  
  15.         if(typeId==1){  
  16.             System.out.println("水果的蘋果的口感是:酸澀");  
  17.         }  
  18.         else if(typeId==2){  
  19.             System.out.println("水果的蘋果的口感是:綿甜");  
  20.         }  
  21.         else{  
  22.             System.out.println("水果的蘋果的口感是:脆甜");  
  23.         }  
  24.     }  
  25. }  
  26.   
  27. public class FruitRun {  
  28.   
  29.     /** 
  30.      * @param args 
  31.      */  
  32.     public static void main(String[] args) {  
  33.         Apple apple=new Apple();  
  34.         apple.displayName();      
  35.           
  36.         AppleService appleService=new AppleService();  
  37.         appleService.showTaste();  
  38.         appleService.showTaste(0);  
  39.         appleService.showTaste(2);  
  40.     }  
  41.   
  42. }  

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

[java] view plain copy
  1. public class FruitService {  
  2.       
  3.     @SuppressWarnings(value={ "rawtypes""unchecked" })  
  4.     public static  List<Fruit> getFruitList(){  
  5.         List<Fruit> fruitList=new ArrayList();  
  6.         return fruitList;  
  7.     }  
  8.       
  9.     @SuppressWarnings({ "rawtypes""unchecked" })  
  10.     public static  List<Fruit> getFruit(){  
  11.         List<Fruit> fruitList=new ArrayList();  
  12.         return fruitList;  
  13.     }  
  14.   
  15.     @SuppressWarnings("unused")  
  16.     public static void main(String[] args){  
  17.         List<String> strList=new ArrayList<String>();  
  18.     }  
  19. }  

深入理解Java:註解(Annotation)自定義註解入門

要深入學習註解,我們就必須能定義自己的註解,並使用註解,在定義自己的註解之前,我們就必須要了解Java爲我們提供的元註解和相關定義註解的語法。


元註解:

  元註解的作用就是負責註解其他註解。Java5.0定義了4個標準的meta-annotation類型,它們被用來提供對其它 annotation類型作說明。Java5.0定義的元註解:
    1.@Target,
    2.@Retention,
    3.@Documented,
    4.@Inherited
  這些類型和它們所支持的類在java.lang.annotation包中可以找到。下面我們看一下每個元註解的作用和相應分參數的使用說明。


  @Target:

   @Target說明了Annotation所修飾的對象範圍:Annotation可被用於 packages、types(類、接口、枚舉、Annotation類型)、類型成員(方法、構造方法、成員變量、枚舉值)、方法參數和本地變量(如循環變量、catch參數)。在Annotation類型的聲明中使用了target可更加明晰其修飾的目標。

  作用:用於描述註解的使用範圍(即:被描述的註解可以用在什麼地方)

  取值(ElementType)有:

    1.CONSTRUCTOR:用於描述構造器
    2.FIELD:用於描述域
    3.LOCAL_VARIABLE:用於描述局部變量
    4.METHOD:用於描述方法
    5.PACKAGE:用於描述包
    6.PARAMETER:用於描述參數
    7.TYPE:用於描述類、接口(包括註解類型) 或enum聲明

  使用實例:  

複製代碼
@Target(ElementType.TYPE)
public @interface Table {
    /**
     * 數據表名稱註解,默認值爲類名稱
     * @return
     */
    public String tableName() default "className";
}

@Target(ElementType.FIELD)
public @interface NoDBColumn {

}
複製代碼

  註解Table 可以用於註解類、接口(包括註解類型) 或enum聲明,而註解NoDBColumn僅可用於註解類的成員變量。


  @Retention:

  @Retention定義了該Annotation被保留的時間長短:某些Annotation僅出現在源代碼中,而被編譯器丟棄;而另一些卻被編譯在class文件中;編譯在class文件中的Annotation可能會被虛擬機忽略,而另一些在class被裝載時將被讀取(請注意並不影響class的執行,因爲Annotation與class在使用上是被分離的)。使用這個meta-Annotation可以對 Annotation的“生命週期”限制。

  作用:表示需要在什麼級別保存該註釋信息,用於描述註解的生命週期(即:被描述的註解在什麼範圍內有效)

  取值(RetentionPoicy)有:

    1.SOURCE:在源文件中有效(即源文件保留)
    2.CLASS:在class文件中有效(即class保留)
    3.RUNTIME:在運行時有效(即運行時保留)

  Retention meta-annotation類型有唯一的value作爲成員,它的取值來自java.lang.annotation.RetentionPolicy的枚舉類型值。具體實例如下:

複製代碼
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
    public String name() default "fieldName";
    public String setFuncName() default "setField";
    public String getFuncName() default "getField"; 
    public boolean defaultDBValue() default false;
}
複製代碼

   Column註解的的RetentionPolicy的屬性值是RUTIME,這樣註解處理器可以通過反射,獲取到該註解的屬性值,從而去做一些運行時的邏輯處理


  @Documented:

  @Documented用於描述其它類型的annotation應該被作爲被標註的程序成員的公共API,因此可以被例如javadoc此類的工具文檔化。Documented是一個標記註解,沒有成員。

複製代碼
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Column {
    public String name() default "fieldName";
    public String setFuncName() default "setField";
    public String getFuncName() default "getField"; 
    public boolean defaultDBValue() default false;
}
複製代碼

  @Inherited:

  @Inherited 元註解是一個標記註解,@Inherited闡述了某個被標註的類型是被繼承的。如果一個使用了@Inherited修飾的annotation類型被用於一個class,則這個annotation將被用於該class的子類。

  注意:@Inherited annotation類型是被標註過的class的子類所繼承。類並不從它所實現的接口繼承annotation,方法並不從它所重載的方法繼承annotation。

  當@Inherited annotation類型標註的annotation的Retention是RetentionPolicy.RUNTIME,則反射API增強了這種繼承性。如果我們使用java.lang.reflect去查詢一個@Inherited annotation類型的annotation時,反射代碼檢查將展開工作:檢查class和其父類,直到發現指定的annotation類型被發現,或者到達類繼承結構的頂層。

  實例代碼:

複製代碼
/**
 * 
 * @author peida
 *
 */
@Inherited
public @interface Greeting {
    public enum FontColor{ BULE,RED,GREEN};
    String name();
    FontColor fontColor() default FontColor.GREEN;
}
複製代碼

自定義註解:

  使用@interface自定義註解時,自動繼承了java.lang.annotation.Annotation接口,由編譯程序自動完成其他細節。在定義註解時,不能繼承其他的註解或接口。@interface用來聲明一個註解,其中的每一個方法實際上是聲明瞭一個配置參數。方法的名稱就是參數的名稱,返回值類型就是參數的類型(返回值類型只能是基本類型、Class、String、enum)。可以通過default來聲明參數的默認值。

  定義註解格式:
  public @interface 註解名 {定義體}

  註解參數的可支持數據類型:

    1.所有基本數據類型(int,float,boolean,byte,double,char,long,short)
    2.String類型
    3.Class類型
    4.enum類型
    5.Annotation類型
    6.以上所有類型的數組

  Annotation類型裏面的參數該怎麼設定: 
  第一,只能用public或默認(default)這兩個訪問權修飾.例如,String value();這裏把方法設爲defaul默認類型;   
  第二,參數成員只能用基本類型byte,short,char,int,long,float,double,boolean八種基本數據類型和 String,Enum,Class,annotations等數據類型,以及這一些類型的數組.例如,String value();這裏的參數成員就爲String;  
  第三,如果只有一個參數成員,最好把參數名稱設爲"value",後加小括號.例:下面的例子FruitName註解就只有一個參數成員。

  簡單的自定義註解和使用註解實例:

複製代碼
package annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 水果名稱註解
 * @author peida
 *
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
    String value() default "";
}
複製代碼
複製代碼
package annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 水果顏色註解
 * @author peida
 *
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {
    /**
     * 顏色枚舉
     * @author peida
     *
     */
    public enum Color{ BULE,RED,GREEN};
    
    /**
     * 顏色屬性
     * @return
     */
    Color fruitColor() default Color.GREEN;

}
複製代碼
複製代碼
package annotation;

import annotation.FruitColor.Color;

public class Apple {
    
    @FruitName("Apple")
    private String appleName;
    
    @FruitColor(fruitColor=Color.RED)
    private String appleColor;
    
    
    
    
    public void setAppleColor(String appleColor) {
        this.appleColor = appleColor;
    }
    public String getAppleColor() {
        return appleColor;
    }
    
    
    public void setAppleName(String appleName) {
        this.appleName = appleName;
    }
    public String getAppleName() {
        return appleName;
    }
    
    public void displayName(){
        System.out.println("水果的名字是:蘋果");
    }
}
複製代碼

註解元素的默認值:

  註解元素必須有確定的值,要麼在定義註解的默認值中指定,要麼在使用註解時指定,非基本類型的註解元素的值不可爲null。因此, 使用空字符串或0作爲默認值是一種常用的做法。這個約束使得處理器很難表現一個元素的存在或缺失的狀態,因爲每個註解的聲明中,所有元素都存在,並且都具有相應的值,爲了繞開這個約束,我們只能定義一些特殊的值,例如空字符串或者負數,一次表示某個元素不存在,在定義註解時,這已經成爲一個習慣用法。例如:

複製代碼
 1 package annotation;
 2 
 3 import java.lang.annotation.Documented;
 4 import java.lang.annotation.ElementType;
 5 import java.lang.annotation.Retention;
 6 import java.lang.annotation.RetentionPolicy;
 7 import java.lang.annotation.Target;
 8 
 9 /**
10  * 水果供應者註解
11  * @author peida
12  *
13  */
14 @Target(ElementType.FIELD)
15 @Retention(RetentionPolicy.RUNTIME)
16 @Documented
17 public @interface FruitProvider {
18     /**
19      * 供應商編號
20      * @return
21      */
22     public int id() default -1;
23     
24     /**
25      * 供應商名稱
26      * @return
27      */
28     public String name() default "";
29     
30     /**
31      * 供應商地址
32      * @return
33      */
34     public String address() default "";
35 }
複製代碼

 

  定義了註解,並在需要的時候給相關類,類屬性加上註解信息,如果沒有響應的註解信息處理流程,註解可以說是沒有實用價值。如何讓註解真真的發揮作用,主要就在於註解處理方法,下一步我們將學習註解信息的獲取和處理!


深入理解Java:註解(Annotation)--註解處理器

  如果沒有用來讀取註解的方法和工作,那麼註解也就不會比註釋更有用處了。使用註解的過程中,很重要的一部分就是創建於使用註解處理器。Java SE5擴展了反射機制的API,以幫助程序員快速的構造自定義註解處理器。


註解處理器類庫(java.lang.reflect.AnnotatedElement):

  Java使用Annotation接口來代表程序元素前面的註解,該接口是所有Annotation類型的父接口。除此之外,Java在java.lang.reflect 包下新增了AnnotatedElement接口,該接口代表程序中可以接受註解的程序元素,該接口主要有如下幾個實現類:

  Class:類定義
  Constructor:構造器定義
  Field:累的成員變量定義
  Method:類的方法定義
  Package:類的包定義

  java.lang.reflect 包下主要包含一些實現反射功能的工具類,實際上,java.lang.reflect 包所有提供的反射API擴充了讀取運行時Annotation信息的能力。當一個Annotation類型被定義爲運行時的Annotation後,該註解才能是運行時可見,當class文件被裝載時被保存在class文件中的Annotation纔會被虛擬機讀取。
  AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通過反射獲取了某個類的AnnotatedElement對象之後,程序就可以調用該對象的如下四個個方法來訪問Annotation信息:

  方法1:<T extends Annotation> T getAnnotation(Class<T> annotationClass): 返回改程序元素上存在的、指定類型的註解,如果該類型註解不存在,則返回null。
  方法2:Annotation[] getAnnotations():返回該程序元素上存在的所有註解。
  方法3:boolean is AnnotationPresent(Class<?extends Annotation> annotationClass):判斷該程序元素上是否包含指定類型的註解,存在則返回true,否則返回false.
  方法4:Annotation[] getDeclaredAnnotations():返回直接存在於此元素上的所有註釋。與此接口中的其他方法不同,該方法將忽略繼承的註釋。(如果沒有註釋直接存在於此元素上,則返回長度爲零的一個數組。)該方法的調用者可以隨意修改返回的數組;這不會對其他調用者返回的數組產生任何影響。

  一個簡單的註解處理器:  

複製代碼
/***********註解聲明***************/

/**
 * 水果名稱註解
 * @author peida
 *
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
    String value() default "";
}

/**
 * 水果顏色註解
 * @author peida
 *
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {
    /**
     * 顏色枚舉
     * @author peida
     *
     */
    public enum Color{ BULE,RED,GREEN};
    
    /**
     * 顏色屬性
     * @return
     */
    Color fruitColor() default Color.GREEN;

}

/**
 * 水果供應者註解
 * @author peida
 *
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {
    /**
     * 供應商編號
     * @return
     */
    public int id() default -1;
    
    /**
     * 供應商名稱
     * @return
     */
    public String name() default "";
    
    /**
     * 供應商地址
     * @return
     */
    public String address() default "";
}

/***********註解使用***************/

public class Apple {
    
    @FruitName("Apple")
    private String appleName;
    
    @FruitColor(fruitColor=Color.RED)
    private String appleColor;
    
    @FruitProvider(id=1,name="陝西紅富士集團",address="陝西省西安市延安路89號紅富士大廈")
    private String appleProvider;
    
    public void setAppleColor(String appleColor) {
        this.appleColor = appleColor;
    }
    public String getAppleColor() {
        return appleColor;
    }
    
    public void setAppleName(String appleName) {
        this.appleName = appleName;
    }
    public String getAppleName() {
        return appleName;
    }
    
    public void setAppleProvider(String appleProvider) {
        this.appleProvider = appleProvider;
    }
    public String getAppleProvider() {
        return appleProvider;
    }
    
    public void displayName(){
        System.out.println("水果的名字是:蘋果");
    }
}

/***********註解處理器***************/

public class FruitInfoUtil {
    public static void getFruitInfo(Class<?> clazz){
        
        String strFruitName=" 水果名稱:";
        String strFruitColor=" 水果顏色:";
        String strFruitProvicer="供應商信息:";
        
        Field[] fields = clazz.getDeclaredFields();
        
        for(Field field :fields){
            if(field.isAnnotationPresent(FruitName.class)){
                FruitName fruitName = (FruitName) field.getAnnotation(FruitName.class);
                strFruitName=strFruitName+fruitName.value();
                System.out.println(strFruitName);
            }
            else if(field.isAnnotationPresent(FruitColor.class)){
                FruitColor fruitColor= (FruitColor) field.getAnnotation(FruitColor.class);
                strFruitColor=strFruitColor+fruitColor.fruitColor().toString();
                System.out.println(strFruitColor);
            }
            else if(field.isAnnotationPresent(FruitProvider.class)){
                FruitProvider fruitProvider= (FruitProvider) field.getAnnotation(FruitProvider.class);
                strFruitProvicer=" 供應商編號:"+fruitProvider.id()+" 供應商名稱:"+fruitProvider.name()+" 供應商地址:"+fruitProvider.address();
                System.out.println(strFruitProvicer);
            }
        }
    }
}

/***********輸出結果***************/
public class FruitRun {

    /**
     * @param args
     */
    public static void main(String[] args) {
        
        FruitInfoUtil.getFruitInfo(Apple.class);
        
    }

}

====================================
 水果名稱:Apple
 水果顏色:RED
 供應商編號:1 供應商名稱:陝西紅富士集團 供應商地址:陝西省西安市延安路89號紅富士大廈
複製代碼

   Java註解的基礎知識點(見下面導圖)基本都過了一遍,下一篇我們通過設計一個基於註解的簡單的ORM框架,來綜合應用和進一步加深對註解的各個知識點的理解和運用。


java中註解的使用與實例

註解目前非常的流行,很多主流框架都支持註解,而且自己編寫代碼的時候也會盡量的去用註解,一時方便,而是代碼更加簡潔。

     註解的語法比較簡單,除了@符號的使用之外,它基本與Java固有語法一致。Java SE5內置了三種標準註解:

     @Override,表示當前的方法定義將覆蓋超類中的方法。

     @Deprecated,使用了註解爲它的元素編譯器將發出警告,因爲註解@Deprecated是不贊成使用的代碼,被棄用的代碼。

     @SuppressWarnings,關閉不當編譯器警告信息。

     上面這三個註解多少我們都會在寫代碼的時候遇到。Java還提供了4中註解,專門負責新註解的創建。

 

@Target

表示該註解可以用於什麼地方,可能的ElementType參數有:

CONSTRUCTOR:構造器的聲明

FIELD:域聲明(包括enum實例)

LOCAL_VARIABLE:局部變量聲明

METHOD:方法聲明

PACKAGE:包聲明

PARAMETER:參數聲明

TYPE:類、接口(包括註解類型)或enum聲明

@Retention

表示需要在什麼級別保存該註解信息。可選的RetentionPolicy參數包括:

SOURCE:註解將被編譯器丟棄

CLASS:註解在class文件中可用,但會被VM丟棄

RUNTIME:VM將在運行期間保留註解,因此可以通過反射機制讀取註解的信息。

@Document

將註解包含在Javadoc中

@Inherited

允許子類繼承父類中的註解

    

  定義一個註解的方式:   

1 @Target(ElementType.METHOD)
2 @Retention(RetentionPolicy.RUNTIME)
3 public @interface Test {
4  
5 }

除了@符號,註解很像是一個接口。定義註解的時候需要用到元註解,上面用到了@Target@RetentionPolicy,它們的含義在上面的表格中已近給出。 

     在註解中一般會有一些元素以表示某些值。註解的元素看起來就像接口的方法,唯一的區別在於可以爲其制定默認值。沒有元素的註解稱爲標記註解,上面的@Test就是一個標記註解。 

     註解的可用的類型包括以下幾種:所有基本類型、String、Class、enum、Annotation、以上類型的數組形式。元素不能有不確定的值,即要麼有默認值,要麼在使用註解的時候提供元素的值。而且元素不能使用null作爲默認值。註解在只有一個元素且該元素的名稱是value的情況下,在使用註解的時候可以省略“value=”,直接寫需要的值即可。 

     下面看一個定義了元素的註解。

1 @Target(ElementType.METHOD)
2 @Retention(RetentionPolicy.RUNTIME)
3 public @interface UseCase {
4      public String id();
5      public String description() default "no description";
6 }

  定義了註解,必然要去使用註解。 

複製代碼
 1 public class PasswordUtils {
 2      @UseCase(id = 47, description = "Passwords must contain at least one numeric")
 3      public boolean validatePassword(String password) {
 4          return (password.matches("\\w*\\d\\w*"));
 5      }
 6  
 7      @UseCase(id = 48)
 8      public String encryptPassword(String password) {
 9          return new StringBuilder(password).reverse().toString();
10      }
11  }
複製代碼

 

使用註解最主要的部分在於對註解的處理,那麼就會涉及到註解處理器。

     從原理上講,註解處理器就是通過反射機制獲取被檢查方法上的註解信息,然後根據註解元素的值進行特定的處理。

複製代碼
public static void main(String[] args) {
     List<Integer> useCases = new ArrayList<Integer>();
     Collections.addAll(useCases, 47, 48, 49, 50);
     trackUseCases(useCases, PasswordUtils.class);
 }
 
 public static void trackUseCases(List<Integer> useCases, Class<?> cl) {
     for (Method m : cl.getDeclaredMethods()) {
         UseCase uc = m.getAnnotation(UseCase.class);
         if (uc != null) {
             System.out.println("Found Use Case:" + uc.id() + " "
                         + uc.description());
             useCases.remove(new Integer(uc.id()));
         }
     }
     for (int i : useCases) {
         System.out.println("Warning: Missing use case-" + i);
     }
 }
複製代碼

Found Use Case:47 Passwords must contain at least one numeric

Found Use Case:48 no description

Warning: Missing use case-49

Warning: Missing use case-50

 上面的三段代碼結合起來是一個跟蹤項目中用例的簡單例子。

     寫到這裏博主想到結合枚舉、註解、反射、攔截器等內容,是否可以寫一套用戶權限驗證呢?

     將用戶權限用枚舉的方式給出,註解元素表明某個方法必須擁有某些權限才能調用,攔截器攔截請求方法,用戶是否有權限對該方法進行調用,根據用戶不同的權限進行不同的處理。歡迎討論!


註解處理器

如果沒有用來讀取註解的方法和工作,那麼註解也就不會比註釋更有用處了。使用註解的過程中,很重要的一部分就是創建於使用註解處理器。Java SE5擴展了反射機制的API,以幫助程序員快速的構造自定義註解處理器。

註解處理器類庫(java.lang.reflect.AnnotatedElement):
  Java使用Annotation接口來代表程序元素前面的註解,該接口是所有Annotation類型的父接口。除此之外,Java在java.lang.reflect 包下新增了AnnotatedElement接口,該接口代表程序中可以接受註解的程序元素,該接口主要有如下幾個實現類:
  Class:類定義
  Constructor:構造器定義
  Field:累的成員變量定義
  Method:類的方法定義
  Package:類的包定義
  java.lang.reflect 包下主要包含一些實現反射功能的工具類,實際上,java.lang.reflect 包所有提供的反射API擴充了讀取運行時Annotation信息的能力。當一個Annotation類型被定義爲運行時的Annotation後,該註解才能是運行時可見,當class文件被裝載時被保存在class文件中的Annotation纔會被虛擬機讀取。
  AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通過反射獲取了某個類的AnnotatedElement對象之後,程序就可以調用該對象的如下四個個方法來訪問Annotation信息:
  方法1:<T extends Annotation> T getAnnotation(Class<T> annotationClass): 返回改程序元素上存在的、指定類型的註解,如果該類型註解不存在,則返回null。
  方法2:Annotation[] getAnnotations():返回該程序元素上存在的所有註解。
  方法3:boolean is AnnotationPresent(Class<?extends Annotation> annotationClass):判斷該程序元素上是否包含指定類型的註解,存在則返回true,否則返回false.
  方法4:Annotation[] getDeclaredAnnotations():返回直接存在於此元素上的所有註釋。與此接口中的其他方法不同,該方法將忽略繼承的註釋。(如果沒有註釋直接存在於此元素上,則返回長度爲零的一個數組。)該方法的調用者可以隨意修改返回的數組;這不會對其他調用者返回的數組產生任何影響。
  一個簡單的註解處理器:

[java] view plain copy
  1. package student.com.dhp;  
  2.   
  3. import java.lang.annotation.ElementType;  
  4. import java.lang.annotation.Retention;  
  5. import java.lang.annotation.RetentionPolicy;  
  6. import java.lang.annotation.Target;  
  7.   
  8. //用於描述域  
  9. @Target(ElementType.FIELD)  
  10. //運行時纔有效  
  11. @Retention(RetentionPolicy.RUNTIME)  
  12. public @interface Student {  
  13.     public enum Sex {  
  14.         MAN, WOMAN  
  15.     }  
  16.     /** 
  17.      * 1.參數成員只能用基本類型String,Enum,Class,annotations等數據類型,以及這一些類型的數組 
  18.      * 2.只能用public或默認(default)這兩個訪問權修飾. 3.如果只有一個參數成員,最好把參數名稱設爲"value" 
  19.      */  
  20.     String name() default "";  
  21.     public int age() default 18;  
  22.     public Sex sex() default Sex.MAN;  
  23. }  

[java] view plain copy
  1. package student.com.dhp;  
  2.   
  3. public class StudentBean {  
  4.     private String name;  
  5.     private int age;  
  6.     private String sex;  
  7.       
  8.     public String getName() {  
  9.         return name;  
  10.     }  
  11.     public void setName(String name) {  
  12.         this.name = name;  
  13.     }  
  14.     public int getAge() {  
  15.         return age;  
  16.     }  
  17.     public void setAge(int age) {  
  18.         this.age = age;  
  19.     }  
  20.     public String getSex() {  
  21.         return sex;  
  22.     }  
  23.     public void setSex(String sex) {  
  24.         this.sex = sex;  
  25.     }  
  26.       
  27. }  

[java] view plain copy
  1. package student.com.dhp;  
  2.   
  3. import java.lang.reflect.Field;  
  4.   
  5. public class StudentInfoUtil {  
  6.     public static void getStudentInfo(Class<?> clazz) {  
  7.         // 獲取程序對象所有的Field註釋  
  8.         Field[] fields = clazz.getDeclaredFields();  
  9.         for (Field field : fields) {  
  10.             if(field.isAnnotationPresent(Student.class)) {  
  11.                 // 獲取Annotation對象  
  12.                 Student student = field.getAnnotation(Student.class);  
  13.                 System.out.println("姓名:"+student.name()+" 年齡:"+student.age()+" 性別:"+  
  14.                 student.sex());  
  15.             }  
  16.         }  
  17.     }  
  18. }  

[java] view plain copy
  1. package student.com.dhp;  
  2.   
  3. import student.com.dhp.Student.Sex;  
  4.   
  5. public class Test {  
  6.     @Student(name = "Snail", age = 18, sex = Sex.MAN)  
  7.     private StudentBean student01;  
  8.     @Student(name = "Snail2", age = 19, sex = Sex.WOMAN)  
  9.     private StudentBean student02;  
  10.       
  11.     public static void main(String[] args) {  
  12.         StudentInfoUtil.getStudentInfo(Test.class);  
  13.     }  
  14. }  


發佈了22 篇原創文章 · 獲贊 26 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章