java中的注解(Annotation)詳解

前言

最近在學(xué)習(xí)ButterKnife知識(shí),涉及到注解相關(guān)知識(shí),就想系統(tǒng)學(xué)習(xí)下,查看了一些博客,甚至包括java作者Gosling編寫(xiě)的《java編程規(guī)范》第三版中譯版看,看完后依舊不是太懂,直到看到了秒懂甚牲,Java 注解 (Annotation)你可以這樣學(xué)這篇文章,才真正直到注解的用法與作用.然后我結(jié)合Java 注解(Annotation)這篇文章,從自己理解的角度補(bǔ)充了一些點(diǎn)知識(shí).

定義

Java 注解(Annotation)又稱(chēng) Java 標(biāo)注,是 JDK5.0 引入的一種注釋機(jī)制痹扇。

   大白話: 注解好比標(biāo)簽,給需要被標(biāo)記的東西貼上就行了.

看完定義,還是一臉懵逼,這玩意有啥用呢? 沒(méi)啥用我學(xué)它不是浪費(fèi)時(shí)間嘛,下面我們來(lái)看看官方說(shuō)明的用途

用途
  1. 提供信息給編譯器: 編譯器可以利用注解來(lái)探測(cè)錯(cuò)誤和警告信息

    比如我們常見(jiàn)到的 @Override, @Deprecated, @SuppressWarnings 是不是很熟悉的feel?
    
  2. 編譯階段時(shí)的處理: 軟件工具可以用來(lái)利用注解信息來(lái)生成代碼、Html文檔或者做其它相應(yīng)處理馍佑。

    比如我們常用的框架'ButterKnife, EventBus, Retrofit'等,都充斥著大量的注解,查看源碼,了解注解知識(shí)必不可少.
    
  3. 運(yùn)行時(shí)的處理: 某些注解可以在程序運(yùn)行的時(shí)候接受代碼的提取

    運(yùn)行時(shí),通過(guò)動(dòng)態(tài)獲取屬性來(lái)完成一些工作(這個(gè)有點(diǎn)抽象,下面會(huì)有例子說(shuō)明).
    

關(guān)于注解其余的用處,等掌握了注解的詳細(xì)知識(shí),你就可以自由發(fā)揮啦! 現(xiàn)在我們知道注解用很多用處了,那就開(kāi)始學(xué)習(xí)它吧.

注解的語(yǔ)法
 查看了Gosling編寫(xiě)的《java編程規(guī)范》第三版中譯版,注解放在第九章接口這個(gè)章節(jié)里面講解,其解釋如下:

注解類(lèi)型聲明: 是一種特殊的接口聲明,為了把注釋類(lèi)型聲明與普通接口聲明區(qū)分開(kāi),在關(guān)鍵字Interface前面放置了一個(gè)@符號(hào).@”作為前綴聲明 玩裙,向編譯器說(shuō)明,該元素(Annotation)是注解.注解在編譯后忍宋,編譯器會(huì)自動(dòng)繼承java.lang.annotation.Annotation接口

public interface Annotation {
   
    boolean equals(Object obj);
    int hashCode();
    String toString();
    /**
     * Returns the annotation type of this annotation.
     * @return the annotation type of this annotation
     */
    Class<? extends Annotation> annotationType();
}

使用自定義注解時(shí)應(yīng)該注意以下幾個(gè)問(wèn)題:

  • 定義注解(@interface )時(shí)帝火,不能繼承其他的注解或接口,編譯器會(huì)自動(dòng)繼承java.lang.annotation.Annotation接口
  • 只能用public或默認(rèn)(default)這兩個(gè)訪問(wèn)權(quán)修飾
  • 參數(shù)成員只能用基本類(lèi)型byte,short,char,int,long,float,double,boolean八種基本數(shù)據(jù)類(lèi)型和 String,Enum,Class,annotations等數(shù)據(jù)類(lèi)型,以及這一些類(lèi)型的數(shù)組

這里說(shuō)參數(shù)成員可能有些不懂,后面注解屬性會(huì)講到,這兒主要是先列出注解的語(yǔ)法,同比與類(lèi),接口,枚舉等,它們都有自己的語(yǔ)法規(guī)則.

 *
 * @author  Peter von der Ah&eacute;
 * @author  Joshua Bloch
 * @jls 9.6.1.4 @Override
 * @since 1.5
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

點(diǎn)開(kāi)Override 的源碼查看,確實(shí)如此,interface前面加了個(gè)@符號(hào). 那么,下一個(gè)問(wèn)題來(lái)了,我定義了一個(gè)這樣的注解,它到底怎么工作呢?

public @interface TestAnnotation {
}

既然我們使用注解這個(gè)類(lèi)型,自然要遵從人家的規(guī)則.java定義了幾個(gè)元注解,用于注解到注解上的注解,它規(guī)定了注解的使用范圍,生命周期等等, 元標(biāo)簽有 @Retention溜徙、@Documented、@Target犀填、@Inherited蠢壹、@Repeatable 5 種。以及預(yù)置的注解@Override,@Deprecated,@SuppressWarnings,@SafeVarargs,FunctionalInterface

這兒我舉個(gè)栗子幫助理解下:
地球上的每個(gè)人都是人,從出生就被貼上了很多標(biāo)簽; 現(xiàn)在假設(shè)我們有如下幾個(gè)元標(biāo)簽以及內(nèi)置標(biāo)簽(這些都是地球人公認(rèn)的東西,也是大家遵循的規(guī)則,好比我們java開(kāi)發(fā)遵循java語(yǔ)法一樣)

元標(biāo)簽
@Color: 有wihte, yellow, black三個(gè)值,---------------類(lèi)比于元注解 @Retention
@Aera : 有Asia(亞洲),Europe(歐洲),North America(北美洲),South America(南美洲),Africa(非洲),Oceania(大洋洲),Antarctica(南極洲)------------類(lèi)比于@Target

內(nèi)置標(biāo)簽
@China, @English, @American 等等

此外我們遵循注解的語(yǔ)法,定義Otaku(宅男)這一標(biāo)簽

public @Interface Otaku{

}

好了,宅男定義完了,現(xiàn)在我們來(lái)給一個(gè)人來(lái)打標(biāo)簽

@Aera(Europe)
@Color(yellow)
@Otaku
public @Interface People{

}

現(xiàn)在我們就知道了,這個(gè)人是歐洲的黃種宅男,Otaku(宅男)就是我們的自定義標(biāo)簽,它與元標(biāo)簽,內(nèi)置標(biāo)簽是同一層級(jí)關(guān)系,都是java.lang.annotation.Annotation接口的子類(lèi),只不過(guò)是我們劃分的維度不一樣..元標(biāo)簽,內(nèi)置標(biāo)簽是jdk定義的,而Otaku(宅男)是我們自己定義.所以一個(gè)人可以從不同維度打很多個(gè)標(biāo)簽.


標(biāo)簽.png

Annotation.png

現(xiàn)在我們已經(jīng)了解了注解的語(yǔ)法規(guī)則與元注解,內(nèi)置注解,自定義注解的關(guān)系了,接下來(lái)我們就詳細(xì)看看他們的作用吧.

元注解
  1. @Retention

etention 的英文意為保留期的意思九巡。當(dāng) @Retention 應(yīng)用到一個(gè)注解上的時(shí)候图贸,它解釋說(shuō)明了這個(gè)注解的生命周期。它的取值如下:

  • RetentionPolicy.SOURCE 注解只在源碼階段保留冕广,在編譯器進(jìn)行編譯時(shí)它將被丟棄忽視疏日。
  • RetentionPolicy.CLASS 注解只被保留到編譯進(jìn)行的時(shí)候,它并不會(huì)被加載到 JVM 中撒汉。
  • RetentionPolicy.RUNTIME 注解可以保留到程序運(yùn)行的時(shí)候沟优,它會(huì)被加載進(jìn)入到 JVM 中,所以在程序運(yùn)行時(shí)可以獲取到它們睬辐。
  1. @Documented

這個(gè)元注解肯定是和文檔有關(guān)挠阁。它的作用是能夠?qū)⒆⒔庵械脑匕?Javadoc 中去宾肺。

  1. @Target

指定了注解運(yùn)用的地方。類(lèi)比到標(biāo)簽鹃唯,原本標(biāo)簽是你想張貼到哪個(gè)地方就到哪個(gè)地方爱榕,但是因?yàn)?@Target 的存在瓣喊,它張貼的地方就非常具體了坡慌,比如只能張貼到方法上、類(lèi)上藻三、方法參數(shù)上等等洪橘。@Target 有下面的取值:

 * ElementType.ANNOTATION_TYPE---------------------給注解進(jìn)行注解
* ElementType.CONSTRUCTOR --------------------------給構(gòu)造方法進(jìn)行注解
* ElementType.FIELD ---------------------------------給屬性進(jìn)行注解
* ElementType.LOCAL_VARIABLE  ----------------------給局部變量進(jìn)行注解
* ElementType.METHOD  -----------------------------------給方法進(jìn)行注解
* ElementType.PACKAGE  ----------------------------------給一個(gè)包進(jìn)行注解
* ElementType.PARAMETER  ------------------------------給一個(gè)方法內(nèi)的參數(shù)進(jìn)行注解
* ElementType.TYPE  ------------------------------------給一個(gè)類(lèi)型進(jìn)行注解,比如類(lèi)棵帽、接口熄求、枚舉
  1. @Inherited
    Inherited 是繼承的意思,但是它并不是說(shuō)注解本身可以繼承逗概,而是說(shuō)如果一個(gè)超類(lèi)被 @Inherited 注解過(guò)的注解進(jìn)行注解的話弟晚,那么如果它的子類(lèi)沒(méi)有被任何注解應(yīng)用的話,那么這個(gè)子類(lèi)就繼承了超類(lèi)的注解逾苫。
    說(shuō)的比較抽象卿城。代碼來(lái)解釋。
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface Test {}


@Test
public class A {}


public class B extends A {}

注解 Test 被 @Inherited 修飾铅搓,之后類(lèi) A 被 Test 注解瑟押,類(lèi) B 繼承 A,類(lèi) B 也擁有 Test 這個(gè)注解。
可以這樣理解:

老子非常有錢(qián)星掰,所以人們給他貼了一張標(biāo)簽叫做富豪多望。
老子的兒子長(zhǎng)大后,只要沒(méi)有和老子斷絕父子關(guān)系氢烘,雖然別人沒(méi)有給他貼標(biāo)簽怀偷,但是他自然也是富豪。
老子的孫子長(zhǎng)大了播玖,自然也是富豪椎工。
這就是人們口中戲稱(chēng)的富一代,富二代黎棠,富三代晋渺。雖然叫法不同,好像好多個(gè)標(biāo)簽脓斩,但其實(shí)事情的本質(zhì)也就是他們有一張共同的標(biāo)簽木西,也就是老子身上的那張富豪的標(biāo)簽。

  1. @Repeatable

Repeatable 自然是可重復(fù)的意思随静。@Repeatable 是 Java 1.8 才加進(jìn)來(lái)的八千,所以算是一個(gè)新的特性吗讶。
什么樣的注解會(huì)多次應(yīng)用呢?通常是注解的值可以同時(shí)取多個(gè)恋捆。

舉個(gè)例子照皆,一個(gè)人他既是程序員又是產(chǎn)品經(jīng)理,同時(shí)他還是個(gè)畫(huà)家。


@interface Persons {
    Person[]  value();
}


@Repeatable(Persons.class)
@interface Person{
    String role default "";
}


@Person(role="artist")
@Person(role="coder")
@Person(role="PM")
public class SuperMan{
    
}

注意上面的代碼沸停,@Repeatable 注解了 Person膜毁。而 @Repeatable 后面括號(hào)中的類(lèi)相當(dāng)于一個(gè)容器注解。
什么是容器注解呢愤钾?就是用來(lái)存放其它注解的地方瘟滨。它本身也是一個(gè)注解。
我們?cè)倏纯创a中的相關(guān)容器注解能颁。

@interface Persons {
    Person[]  value();
}

按照規(guī)定杂瘸,它里面必須要有一個(gè) value 的屬性,屬性類(lèi)型是一個(gè)被 @Repeatable 注解過(guò)的注解數(shù)組伙菊,注意它是數(shù)組败玉。

如果不好理解的話,可以這樣理解镜硕。Persons 是一張總的標(biāo)簽运翼,上面貼滿了 Person 這種同類(lèi)型但內(nèi)容不一樣的標(biāo)簽。把 Persons 給一個(gè) SuperMan 貼上谦疾,相當(dāng)于同時(shí)給他貼了程序員南蹂、產(chǎn)品經(jīng)理、畫(huà)家的標(biāo)簽念恍。

我們可能對(duì)于 @Person(role=“PM”) 括號(hào)里面的內(nèi)容感興趣六剥,它其實(shí)就是給 Person 這個(gè)注解的 role 屬性賦值為 PM ,大家不明白正常峰伙,請(qǐng)看下面注解的屬性這一塊疗疟。

注解屬性(★★★)

注解的屬性也叫做成員變量。注解只有成員變量瞳氓,沒(méi)有方法策彤。注解的成員變量在注解的定義中以“無(wú)形參的方法”形式來(lái)聲明,其方法名定義了該成員變量的名字匣摘,其返回值定義了該成員變量的類(lèi)型店诗。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
    
    int id();    //注意,id是成員變量,返回值是int類(lèi)型,它是以方法名定義了該成員變量的名字
    
    String msg();

}

上面代碼定義了 TestAnnotation 這個(gè)注解中擁有 id 和 msg 兩個(gè)屬性。在使用的時(shí)候音榜,我們應(yīng)該給它們進(jìn)行賦值庞瘸。賦值的方式是在注解的括號(hào)內(nèi)以 value="" 形式,多個(gè)屬性之前用 赠叼,隔開(kāi)擦囊。

@TestAnnotation(id=3,msg="hello annotation")
public class Test {

}

需要注意的是违霞,在注解中定義屬性時(shí)它的類(lèi)型必須是 8 種基本數(shù)據(jù)類(lèi)型外加 類(lèi)、接口瞬场、注解及它們的數(shù)組买鸽。
注解中屬性可以有默認(rèn)值,默認(rèn)值需要用 default 關(guān)鍵值指定贯被。比如:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
    
    public int id() default -1;
    
    public String msg() default "Hi";

}

因?yàn)橛心J(rèn)值眼五,所以無(wú)需要再在 @TestAnnotation 后面的括號(hào)里面進(jìn)行賦值了,這一步可以省略刃榨。

@TestAnnotation()
public class Test {
}

另外弹砚,還有一種情況双仍。如果一個(gè)注解內(nèi)僅僅只有一個(gè)名字為 value 的屬性時(shí)枢希,應(yīng)用這個(gè)注解時(shí)可以直接接屬性值填寫(xiě)到括號(hào)內(nèi)。

public @interface Check {
    String value();
}

//簡(jiǎn)單使用方式如下: 

@Check("hi")
int a;

//效果等同于
@Check(value="hi")
int a;

最后朱沃,還需要注意的一種情況是一個(gè)注解沒(méi)有任何屬性苞轿。比如

public @interface Perform {
}


//使用如下
@Perform
public void testMethod(){}

無(wú)屬性的注解,括號(hào)可以省略. 這個(gè)例子很常見(jiàn),比如我們常見(jiàn)的內(nèi)置注解@Override,你加不加()都不影響.

內(nèi)置注解
  1. @Override
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

很常見(jiàn),大家都熟悉,提示子類(lèi)要復(fù)寫(xiě)父類(lèi)中被 @Override 修飾的方法,作用于方法上,生命周期:存在于源代碼

  1. @Deprecated
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}

這個(gè)元素是用來(lái)標(biāo)記過(guò)時(shí)的元素,想必大家在日常開(kāi)發(fā)中經(jīng)常碰到逗物。編譯器在編譯階段遇到這個(gè)注解時(shí)會(huì)發(fā)出提醒警告搬卒,告訴開(kāi)發(fā)者正在調(diào)用一個(gè)過(guò)時(shí)的元素比如過(guò)時(shí)的方法、過(guò)時(shí)的類(lèi)翎卓、過(guò)時(shí)的成員變量契邀。

  1. @SuppressWarnings
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}

阻止警告的意思。之前說(shuō)過(guò)調(diào)用被 @Deprecated 注解的方法后失暴,編譯器會(huì)警告提醒坯门,而有時(shí)候開(kāi)發(fā)者會(huì)忽略這種警告,他們可以在調(diào)用的地方通過(guò) @SuppressWarnings 達(dá)到目的逗扒。

  1. @SafeVarargs

參數(shù)安全類(lèi)型注解古戴。它的目的是提醒開(kāi)發(fā)者不要用參數(shù)做一些不安全的操作,它的存在會(huì)阻止編譯器產(chǎn)生 unchecked 這樣的警告。它是在 Java 1.7 的版本中加入的矩肩。

@SafeVarargs // Not actually safe!
    static void m(List<String>... stringLists) {
    Object[] array = stringLists;
    List<Integer> tmpList = Arrays.asList(42);
    array[0] = tmpList; // Semantically invalid, but compiles without warnings
    String s = stringLists[0].get(0); // Oh no, ClassCastException at runtime!
}

上面的代碼中现恼,編譯階段不會(huì)報(bào)錯(cuò),但是運(yùn)行時(shí)會(huì)拋出 ClassCastException 這個(gè)異常黍檩,所以它雖然告訴開(kāi)發(fā)者要妥善處理叉袍,但是開(kāi)發(fā)者自己還是搞砸了。

Java 官方文檔說(shuō)刽酱,未來(lái)的版本會(huì)授權(quán)編譯器對(duì)這種不安全的操作產(chǎn)生錯(cuò)誤警告喳逛。

  1. @FunctionalInterface

函數(shù)式接口注解,這個(gè)是 Java 1.8 版本引入的新特性肛跌。函數(shù)式編程很火艺配,所以 Java 8 也及時(shí)添加了這個(gè)特性察郁。函數(shù)式接口 (Functional Interface) 就是一個(gè)具有一個(gè)方法的普通接口。

上面我們已經(jīng)弄清楚了注解的定義方法,以及作用域,生命周期等等,那么,我們使用注解后,如何來(lái)獲取注解里面的內(nèi)容呢?

要想正確檢閱注解转唉,離不開(kāi)一個(gè)手段皮钠,那就是反射

注解與反射

注解通過(guò)反射獲取赠法。首先可以通過(guò) Class 對(duì)象的 isAnnotationPresent() 方法判斷它是否應(yīng)用了某個(gè)注解

public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {}

然后通過(guò) getAnnotation() 方法來(lái)獲取 Annotation 對(duì)象麦轰。

//返回指定類(lèi)型的注解
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}

或者是 getAnnotations() 方法。

//返回注解到這個(gè)元素上的所有注解砖织。適用于含有多個(gè)注解的情況
public Annotation[] getAnnotations() {}

如果獲取到的 Annotation 如果不為 null款侵,則就可以調(diào)用它們的屬性方法了。比如

@TestAnnotation()
public class Test {
    
    public static void main(String[] args) {
        
        boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
        
        if ( hasAnnotation ) {
            TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
        
            System.out.println("id:"+testAnnotation.id());
            System.out.println("msg:"+testAnnotation.msg());
        }

    }

}


程序運(yùn)行結(jié)果如下:

id:-1
msg:

上面的例子中侧纯,只是檢閱出了注解在類(lèi)上的注解新锈,其實(shí)屬性、方法上的注解照樣是可以的眶熬。同樣還是要假手于反射妹笆。

@TestAnnotation(msg="hello")
public class Test {
    
    @Check(value="hi")
    int a;
    
    
    @Perform
    public void testMethod(){}
    
    
    @SuppressWarnings("deprecation")
    public void test1(){
        Hero hero = new Hero();
        hero.say();
        hero.speak();
    }


    public static void main(String[] args) {
        
        boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
        
        if ( hasAnnotation ) {
            TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
            //獲取類(lèi)的注解
            System.out.println("id:"+testAnnotation.id());
            System.out.println("msg:"+testAnnotation.msg());
        }
        
        
        try {
            Field a = Test.class.getDeclaredField("a");
            a.setAccessible(true);
            //獲取一個(gè)成員變量上的注解
            Check check = a.getAnnotation(Check.class);
            
            if ( check != null ) {
                System.out.println("check value:"+check.value());
            }
            
            Method testMethod = Test.class.getDeclaredMethod("testMethod");
            
            if ( testMethod != null ) {
                // 獲取方法中的注解
                Annotation[] ans = testMethod.getAnnotations();
                for( int i = 0;i < ans.length;i++) {
                    System.out.println("method testMethod annotation:"+ans[i].annotationType().getSimpleName());
                }
            }
        } catch (NoSuchFieldException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.out.println(e.getMessage());
        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.out.println(e.getMessage());
        } catch (NoSuchMethodException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.out.println(e.getMessage());
        }
        
        

    }

}


運(yùn)行結(jié)果如下:

id:-1
msg:hello
check value:hi
method testMethod annotation:Perform

需要注意的是,如果一個(gè)注解要在運(yùn)行時(shí)被成功提取娜氏,那么 @Retention(RetentionPolicy.RUNTIME) 是必須的拳缠。

當(dāng)開(kāi)發(fā)者使用了自定義的Annotation 修飾了類(lèi)、方法贸弥、Field 等成員之后窟坐,這些 Annotation 不會(huì)自己生效,必須由開(kāi)發(fā)者提供相應(yīng)的代碼來(lái)提取并處理 Annotation 信息绵疲。這些處理提取和處理 Annotation 的代碼統(tǒng)稱(chēng)為 APT(Annotation Processing Tool)哲鸳。

回到我們前面所說(shuō)的注解的作用第三條:

運(yùn)行時(shí)的處理: 某些注解可以在程序運(yùn)行的時(shí)候接受代碼的提取

例子如下:

我要寫(xiě)一個(gè)測(cè)試框架,測(cè)試程序員的代碼有無(wú)明顯的異常最岗。

—— 程序員 A : 我寫(xiě)了一個(gè)類(lèi)帕胆,它的名字叫做 NoBug,因?yàn)樗械姆椒ǘ紱](méi)有錯(cuò)誤般渡。
—— 我:自信是好事懒豹,不過(guò)為了防止意外,讓我測(cè)試一下如何驯用?
—— 程序員 A: 怎么測(cè)試脸秽?
—— 我:把你寫(xiě)的代碼的方法都加上 @Jiecha 這個(gè)注解就好了。
—— 程序員 A: 好的蝴乔。

public class NoBug {
   
   @Jiecha
   public void suanShu(){
       System.out.println("1234567890");
   }
   @Jiecha
   public void jiafa(){
       System.out.println("1+1="+1+1);
   }
   @Jiecha
   public void jiefa(){
       System.out.println("1-1="+(1-1));
   }
   @Jiecha
   public void chengfa(){
       System.out.println("3 x 5="+ 3*5);
   }
   @Jiecha
   public void chufa(){
       System.out.println("6 / 0="+ 6 / 0);
   }
   
   public void ziwojieshao(){
       System.out.println("我寫(xiě)的程序沒(méi)有 bug!");
   }

}

上面的代碼记餐,有些方法上面運(yùn)用了 @Jiecha 注解。這個(gè)注解是我寫(xiě)的測(cè)試軟件框架中定義的注解

@Retention(RetentionPolicy.RUNTIME)
public @interface Jiecha {

}

然后薇正,我再編寫(xiě)一個(gè)測(cè)試類(lèi) TestTool 就可以測(cè)試 NoBug 相應(yīng)的方法了片酝。

public class TestTool {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        
        NoBug testobj = new NoBug();
        
        Class clazz = testobj.getClass();
        
        Method[] method = clazz.getDeclaredMethods();
        //用來(lái)記錄測(cè)試產(chǎn)生的 log 信息
        StringBuilder log = new StringBuilder();
        // 記錄異常的次數(shù)
        int errornum = 0;
        
        for ( Method m: method ) {
            // 只有被 @Jiecha 標(biāo)注過(guò)的方法才進(jìn)行測(cè)試
            if ( m.isAnnotationPresent( Jiecha.class )) {
                try {
                    m.setAccessible(true);
                    m.invoke(testobj, null);
                
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    //e.printStackTrace();
                    errornum++;
                    log.append(m.getName());
                    log.append(" ");
                    log.append("has error:");
                    log.append("\n\r  caused by ");
                    //記錄測(cè)試過(guò)程中囚衔,發(fā)生的異常的名稱(chēng)
                    log.append(e.getCause().getClass().getSimpleName());
                    log.append("\n\r");
                    //記錄測(cè)試過(guò)程中,發(fā)生的異常的具體信息
                    log.append(e.getCause().getMessage());
                    log.append("\n\r");
                } 
            }
        }
        
        
        log.append(clazz.getSimpleName());
        log.append(" has  ");
        log.append(errornum);
        log.append(" error.");
        
        // 生成測(cè)試報(bào)告
        System.out.println(log.toString());

    }

}


測(cè)試的結(jié)果是:

1234567890
1+1=11
1-1=0
3 x 5=15
chufa has error:

  caused by ArithmeticException

/ by zero

NoBug has  1 error.

其實(shí),個(gè)人覺(jué)得,它更大的作用在于第二點(diǎn)

編譯階段時(shí)的處理: 軟件工具可以用來(lái)利用注解信息來(lái)生成代碼雕沿、Html文檔或者做其它相應(yīng)處理练湿。

很多優(yōu)秀的框架正是應(yīng)用到了這點(diǎn),減少了很多重復(fù)性的開(kāi)發(fā)工作.

最后

本文幾乎大多是轉(zhuǎn)自秒懂,Java 注解 (Annotation)你可以這樣學(xué)這篇文章,外加Java 注解(Annotation)結(jié)合并補(bǔ)充了自己的理解. 再次對(duì)作者表示敬意,本文主要記錄自己學(xué)習(xí)理解過(guò)程,加深自己對(duì)這一知識(shí)的理解, 并不作為任何商業(yè)用途!

參考文獻(xiàn)

秒懂审轮,Java 注解 (Annotation)你可以這樣學(xué)
Java 注解(Annotation)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末肥哎,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子疾渣,更是在濱河造成了極大的恐慌篡诽,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,816評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件榴捡,死亡現(xiàn)場(chǎng)離奇詭異杈女,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)薄疚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)碧信,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人街夭,你說(shuō)我怎么就攤上這事□锓ぃ” “怎么了板丽?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,300評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)趁尼。 經(jīng)常有香客問(wèn)我埃碱,道長(zhǎng),這世上最難降的妖魔是什么酥泞? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,780評(píng)論 1 285
  • 正文 為了忘掉前任砚殿,我火速辦了婚禮,結(jié)果婚禮上芝囤,老公的妹妹穿的比我還像新娘似炎。我一直安慰自己,他們只是感情好悯姊,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布羡藐。 她就那樣靜靜地躺著,像睡著了一般悯许。 火紅的嫁衣襯著肌膚如雪仆嗦。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 50,084評(píng)論 1 291
  • 那天先壕,我揣著相機(jī)與錄音瘩扼,去河邊找鬼谆甜。 笑死,一個(gè)胖子當(dāng)著我的面吹牛集绰,可吹牛的內(nèi)容都是我干的店印。 我是一名探鬼主播,決...
    沈念sama閱讀 39,151評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼倒慧,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼按摘!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起纫谅,我...
    開(kāi)封第一講書(shū)人閱讀 37,912評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤炫贤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后付秕,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體兰珍,經(jīng)...
    沈念sama閱讀 44,355評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評(píng)論 2 327
  • 正文 我和宋清朗相戀三年询吴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了掠河。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,809評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡猛计,死狀恐怖唠摹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情奉瘤,我是刑警寧澤勾拉,帶...
    沈念sama閱讀 34,504評(píng)論 4 334
  • 正文 年R本政府宣布,位于F島的核電站盗温,受9級(jí)特大地震影響劫窒,放射性物質(zhì)發(fā)生泄漏神年。R本人自食惡果不足惜吃既,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評(píng)論 3 317
  • 文/蒙蒙 一渺杉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧砚偶,春花似錦批销、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至酒请,卻和暖如春骡技,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,121評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工布朦, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留囤萤,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,628評(píng)論 2 362
  • 正文 我出身青樓是趴,卻偏偏與公主長(zhǎng)得像涛舍,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子唆途,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評(píng)論 2 351

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