前言
最近在學(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ō)明的用途
用途
-
提供信息給編譯器: 編譯器可以利用注解來(lái)探測(cè)錯(cuò)誤和警告信息
比如我們常見(jiàn)到的 @Override, @Deprecated, @SuppressWarnings 是不是很熟悉的feel?
-
編譯階段時(shí)的處理: 軟件工具可以用來(lái)利用注解信息來(lái)生成代碼、Html文檔或者做其它相應(yīng)處理馍佑。
比如我們常用的框架'ButterKnife, EventBus, Retrofit'等,都充斥著大量的注解,查看源碼,了解注解知識(shí)必不可少.
-
運(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é
* @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)簽.
現(xiàn)在我們已經(jīng)了解了注解的語(yǔ)法規(guī)則與元注解,內(nèi)置注解,自定義注解的關(guān)系了,接下來(lái)我們就詳細(xì)看看他們的作用吧.
元注解
- @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í)可以獲取到它們睬辐。
- @Documented
這個(gè)元注解肯定是和文檔有關(guān)挠阁。它的作用是能夠?qū)⒆⒔庵械脑匕?Javadoc 中去宾肺。
- @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)棵帽、接口熄求、枚舉
- @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)簽。
- @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)置注解
- @Override
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
很常見(jiàn),大家都熟悉,提示子類(lèi)要復(fù)寫(xiě)父類(lèi)中被 @Override 修飾的方法,作用于方法上,生命周期:存在于源代碼
- @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í)的成員變量契邀。
- @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á)到目的逗扒。
- @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ò)誤警告喳逛。
- @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è)用途!