Java 注解基礎(chǔ)知識(shí)


備注: 本文大部分內(nèi)容及代碼均來(lái)自網(wǎng)上,具體引用看末尾 8.參考引用**


  1. 概念及作用
  2. JDK注解
  3. 元注解
  4. 自定義注解
  5. 注解處理器
  6. Java 8 中注解新特性
  7. 注解的優(yōu)缺點(diǎn)及與XML的比較
  8. 參考引用

1. 概念及作用

  • 1.1 概念

    • 注解即元數(shù)據(jù),就是源代碼的元數(shù)據(jù)
    • 注解在代碼中添加信息提供了一種形式化的方法,可以在后續(xù)中更方便的 使用這些數(shù)據(jù)
    • Annotation是一種應(yīng)用于類糊饱、方法、參數(shù)仪缸、變量谣膳、構(gòu)造器及包聲明中的特殊修飾符竟终。它是一種由JSR-175標(biāo)準(zhǔn)選擇用來(lái)描述元數(shù)據(jù)的一種工具榴捡。
  • 1.2 作用

    • a. 生成文檔
    • b. 跟蹤代碼依賴性诱桂,實(shí)現(xiàn)替代配置文件功能,減少配置帘瞭。如Spring中的一些注解
    • c. 在編譯時(shí)進(jìn)行格式檢查淑掌,如@Override等
  • 1.3 意義

    • 注解之前,XML被廣泛的應(yīng)用于描述元數(shù)據(jù),XML的維護(hù)越來(lái)越糟糕
    • 在需要緊耦合的地方,比XML該容易維護(hù),閱讀更方便
    • 在需要比較多參數(shù)設(shè)置時(shí),使用xml更方便,而在將某個(gè)方法聲明為服務(wù)時(shí)這種緊耦合的情況下,比較適合注解
    • XML是松耦合的蝶念,注解是緊耦合的
    • 對(duì)于“XML VS 注解” 抛腕,可以google了解一下
    • 對(duì)于XML和注解的使用,要具體問(wèn)題具體分析
    • Java的annotation沒(méi)有行為芋绸,只能有數(shù)據(jù),實(shí)際上就是一組鍵值對(duì)而已担敌。通過(guò)解析(parse)Class文件就能把一個(gè)annotation需要的鍵值對(duì)都找出來(lái)

2. JDK注解

  • 2.1 Override: 保證編譯時(shí) 要重寫方法的正確性
  • 2.2 Deprected: 提示該方法已經(jīng)過(guò)時(shí)
  • 2.3 SuppressWarnings: 關(guān)閉特定警告信息,參數(shù)如下:
    • all: to suppress all warnings
    • boxing: to suppress warnings relative to boxing/unboxing operations
    • cast: to suppress warnings relative to cast operations
    • dep-ann: to suppress warnings relative to deprecated annotation
    • deprecation: to suppress warnings relative to deprecation
    • fallthrough: to suppress warnings relative to missing breaks in switch statements
    • finally: to suppress warnings relative to finally block that don’t return
    • hiding: to suppress warnings relative to locals that hide variable
    • incomplete-switch: to suppress warnings relative to missing entries in a switch statement (enum case)
    • nls: to suppress warnings relative to non-nls string literals
    • null: to suppress warnings relative to null analysis
    • rawtypes: to suppress warnings relative to un-specific types when using generics on class params
    • restriction: to suppress warnings relative to usage of discouraged or forbidden references
    • serial: to suppress warnings relative to missing serialVersionUID field for a serializable class
    • static-access: to suppress warnings relative to incorrect static access
    • synthetic-access: to suppress warnings relative to unoptimized access from inner classes
    • unchecked: to suppress warnings relative to unchecked operations
    • unqualified-field-access: to suppress warnings relative to field access unqualified
    • unused: to suppress warnings relative to unused code

3. 元注解

  • 2.1 定義
    • 負(fù)責(zé)注解其他注解
  • 2.2 四種元注解
    • @Documented
    • @Target
    • @Retention
    • @Inherited
  • 2.3 @Documented
    • 用于描述其他類型的annotation應(yīng)該被作為被標(biāo)注的程序成員的公共API
    • 可以用于javadoc 此類的工具文檔化
    • Document是一個(gè)標(biāo)記注解,沒(méi)有成員
  • 2.4 @Target
    • 說(shuō)明Annotation所修飾的對(duì)象范圍,即描述注解的使用范圍
    • Annotation可被用于packages,types(類,接口,枚舉,Annotation類型),類型成員(方法,構(gòu)造方法,成員變量,枚舉值),方法參數(shù)和本地變量(如循環(huán)變量,catch參數(shù))
    • 在Annotation類型的聲明中使用了target可更加明晰其修飾目標(biāo)
    • 取值有:
類型 用途
CONSTRUCTOR 用于描述構(gòu)造器
FIELD 用于描述域
LOCAL_VARIABLE 用于描述局部變量
METHOD 用于描述方法
PACKAGE 用于描述包
PARAMETER 用于描述參數(shù)
TYPE 用于描述類摔敛、接口(包括注解類型) 或enum聲明
  • 2.5 @Retention
    • 定義了Annotation被保留的時(shí)間長(zhǎng)短
    • 表示需要在什么級(jí)別保存該注釋信息,用于描述注解的生命周期(即被描述的注解在什么范圍內(nèi)有效)
    • Retention meta-annotation類型有唯一的value作為成員,它的取值來(lái)自java.lang.annotation.RetentionPolicy的枚舉類型值全封。具體實(shí)例:
類型 用途 說(shuō)明
SOURCE 在源文件中有效(即源文件保留) 僅出現(xiàn)在源代碼中马昙,而被編譯器丟棄
CLASS 在class文件中有效(即class保留) 被編譯在class文件中
RUNTIME 在運(yùn)行時(shí)有效(即運(yùn)行時(shí)保留) 編譯在class文件中
  • 2.6 @Inherited
    • 是一個(gè)標(biāo)記注解
    • 闡述了某個(gè)被標(biāo)注的類型是被繼承的
    • 使用了@Inherited修飾的annotation類型被用于一個(gè)class,則這個(gè)annotation將被用于該class的子類
    • @Inherited annotation類型是被標(biāo)注過(guò)的class的子類所繼承。類并不從實(shí)現(xiàn)的接口繼承annotation,方法不從它所重載的方法繼承annotation
    • 當(dāng)@Inherited annotation類型標(biāo)注的annotation的Retention是RetentionPolicy.RUNTIME刹悴,則反射API增強(qiáng)了這種繼承性行楞。如果我們使用java.lang.reflect去查詢一個(gè)@Inherited annotation類型的annotation時(shí),反射代碼檢查將展開工作:檢查class和其父類颂跨,直到發(fā)現(xiàn)指定的annotation類型被發(fā)現(xiàn)敢伸,或者到達(dá)類繼承結(jié)構(gòu)的頂層。

4. 自定義注解

  • 格式
public @interface 注解名{
    定義體
}
  • 注解參數(shù)的可支持?jǐn)?shù)據(jù)類型:
    • 所有基本數(shù)據(jù)類型(int,float,double,boolean,byte,char,long,short)
    • String 類型
    • Class類型
    • enum類型
    • Annotation類型
    • 以上所有類型的數(shù)組
  • 修飾符只能是public 或默認(rèn)(default)
  • 參數(shù)成員只能用基本類型byte,short,int,long,float,double,boolean八種基本類型和String,Enum,Class,annotations及這些類型的數(shù)組
  • 如果只有一個(gè)參數(shù)成員,最好將名稱設(shè)為"value"
  • 注解元素必須有確定的值,可以在注解中定義默認(rèn)值,也可以使用注解時(shí)指定,非基本類型的值不可為null,常使用空字符串或0作默認(rèn)值
  • 在表現(xiàn)一個(gè)元素存在或缺失的狀態(tài)時(shí),定義一下特殊值來(lái)表示,如空字符串或負(fù)值
  • 示例:
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;

/**
 * test注解
 * @author zlw
 *
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestAnnotation {
    /**
     * id
     * @return
     */
    public int id() default -1;
    
    /**
     * name
     * @return
     */
    public String name() default "";
    
}

5. 注解處理器

  • 使用注解的過(guò)程,重要的是創(chuàng)建注解處理器
  • Java SE5 擴(kuò)展了反射機(jī)制的API,以幫助程序員快速的構(gòu)造自定義注解處理器
  • 注解處理器類庫(kù): java.lang.reflect.AnnotatedElement
    • Java使用Annotation接口來(lái)代表程序元素面前的注解,該接口是所有Annotation類型的接口恒削。
    • java.lang.annotation.Annotation 是所有Annotation類型的父接口
    • java.lang.reflect.AnnotatedElement 可以接受注解的程序元素,該接口主要有Class(類定義),Constructor(構(gòu)造器定義),Filed(類的成員變量定義),Method(類的方法定義),Package(類的包定義),
    • java.lang.reflect包所有提供的反射API擴(kuò)充了讀取運(yùn)行時(shí)Annotation信息的能力,當(dāng)一個(gè)Annotation類型被定義為運(yùn)行時(shí)Annotation后,該注解才能運(yùn)行時(shí)可見,當(dāng)class文件被裝載時(shí)被保存在class文件中的Annotation才會(huì)被虛擬機(jī)讀取
    • AnnotatedElement 接口是所有程序元素(Class池颈、Method和Constructor)的父接口,所以程序通過(guò)反射獲取了某個(gè)類的AnnotatedElement對(duì)象之后钓丰,程序就可以調(diào)用該對(duì)象的如下四個(gè)個(gè)方法來(lái)訪問(wèn)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():返回直接存在于此元素上的所有注釋。與此接口中的其他方法不同肥橙,該方法將忽略繼承的注釋魄宏。(如果沒(méi)有注釋直接存在于此元素上,則返回長(zhǎng)度為零的一個(gè)數(shù)組存筏。)該方法的調(diào)用者可以隨意修改返回的數(shù)組宠互;這不會(huì)對(duì)其他調(diào)用者返回的數(shù)組產(chǎn)生任何影響
  • 示例:
/***********注解聲明***************/

/**
 * 水果名稱注解
 * @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;

}

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

/***********注解使用***************/

public class Apple {
    
    @FruitName("Apple")
    private String appleName;
    
    @FruitColor(fruitColor=Color.RED)
    private String appleColor;
    
    @FruitProvider(id=1,name="陜西紅富士集團(tuán)",address="陜西省西安市延安路89號(hào)紅富士大廈")
    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="供應(yīng)商信息:";
        
        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=" 供應(yīng)商編號(hào):"+fruitProvider.id()+" 供應(yīng)商名稱:"+fruitProvider.name()+" 供應(yīng)商地址:"+fruitProvider.address();
                System.out.println(strFruitProvicer);
            }
        }
    }
}

/***********輸出結(jié)果***************/
public class FruitRun {

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

}

====================================
 水果名稱:Apple
 水果顏色:RED
 供應(yīng)商編號(hào):1 供應(yīng)商名稱:陜西紅富士集團(tuán) 供應(yīng)商地址:陜西省西安市延安路89號(hào)紅富士大廈

6. Java 8 中注解新特性

  • @Repeatable 元注解,表示被修飾的注解可以用在同一個(gè)聲明式或者類型加上多個(gè)相同的注解(包含不同的屬性值)
  • @Native 元注解,本地方法
  • java8 中Annotation 可以被用在任何使用 Type 的地方
    //初始化對(duì)象時(shí)
   String myString = new @NotNull String();
   //對(duì)象類型轉(zhuǎn)化時(shí)
   myString = (@NonNull String) str;
   //使用 implements 表達(dá)式時(shí)
   class MyList<T> implements @ReadOnly List<@ReadOnly T>{
   ...
   }
   //使用 throws 表達(dá)式時(shí)
   public void validateValues() throws @Critical ValidationFailedException{
   ...
   }

7. 注解的優(yōu)缺點(diǎn)及與XML的比較

  • 優(yōu):
    • 方便,簡(jiǎn)潔椭坚,配置信息和 Java 代碼放在一起予跌,有助于增強(qiáng)程序的內(nèi)聚性
    • 若要對(duì)配置項(xiàng)進(jìn)行修改,不得不修改 Java 文件善茎,重新編譯打包應(yīng)用
  • 缺:
    • 分散到各個(gè)class文件中券册,維護(hù)性較差
    • 配置項(xiàng)編碼在 Java 文件中,可擴(kuò)展性差
  • 與XML比較:
    • 簡(jiǎn)潔
    • 沒(méi)有XML配置更強(qiáng)大
    • 不便于修改,不便于統(tǒng)一管理

8. 參考引用

  1. 深入理解Java:注解(Annotation)--注解處理器
  2. 深入理解Java:注解(Annotation)自定義注解入門
  3. 深入理解Java:注解(Annotation)基本概念
  4. Java annotation的實(shí)例是什么類的?
  5. java中的注釋
  6. JAVA 注解的幾大作用及使用方法詳解
  7. Java中的注解是如何工作的汁掠?
  8. Java中使用注解和使用配置文件各有什么優(yōu)缺點(diǎn)
  9. JAVA注解
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末略吨,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子考阱,更是在濱河造成了極大的恐慌翠忠,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件乞榨,死亡現(xiàn)場(chǎng)離奇詭異秽之,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)吃既,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門考榨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人鹦倚,你說(shuō)我怎么就攤上這事河质。” “怎么了震叙?”我有些...
    開封第一講書人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵掀鹅,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我媒楼,道長(zhǎng)乐尊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任划址,我火速辦了婚禮扔嵌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘夺颤。我一直安慰自己痢缎,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開白布世澜。 她就那樣靜靜地躺著独旷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪宜狐。 梳的紋絲不亂的頭發(fā)上势告,一...
    開封第一講書人閱讀 49,741評(píng)論 1 289
  • 那天蛇捌,我揣著相機(jī)與錄音抚恒,去河邊找鬼。 笑死络拌,一個(gè)胖子當(dāng)著我的面吹牛俭驮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼混萝,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼遗遵!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起逸嘀,我...
    開封第一講書人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤车要,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后崭倘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體翼岁,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年司光,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了琅坡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡残家,死狀恐怖榆俺,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情坞淮,我是刑警寧澤茴晋,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站碾盐,受9級(jí)特大地震影響晃跺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜毫玖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一掀虎、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧付枫,春花似錦烹玉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至掂榔,卻和暖如春继效,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背装获。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工瑞信, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人穴豫。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓凡简,卻偏偏與公主長(zhǎng)得像逼友,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子秤涩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • Java 注解 Dependency injection 前言 Linus Benedict Torvalds :...
    CodePlayer_Jz閱讀 1,027評(píng)論 0 8
  • 1. 元注解 元注解:用在注解上的注解,java1.5后添加的4個(gè)元注解: @Target @Retention ...
    小菜_charry閱讀 389評(píng)論 1 2
  • 本文章涉及代碼已放到github上annotation-study 1.Annotation為何而來(lái) What:A...
    zlcook閱讀 29,127評(píng)論 15 116
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理帜乞,服務(wù)發(fā)現(xiàn),斷路器筐眷,智...
    卡卡羅2017閱讀 134,628評(píng)論 18 139
  • 2017/5/29起筆 著手寫這文字的時(shí)候黎烈,我已經(jīng)在家待兩個(gè)星期了 兩周前,還在學(xué)校里蹦噠的我匀谣,一下子被砸到深淵谷...
    Daylily_y閱讀 1,043評(píng)論 7 11