詳解Java注解( Annotation )
注解的定義:
注解(Annotation),也叫元數(shù)據(jù)。一種代碼級別的說明。它是JDK1.5及以后版本引入的一個特性,與類、接口、枚舉是在同一個層次。它可以聲明在包、類、字段、方法、局部變量、方法參數(shù)等的前面,用來對這些元素進行說明,注釋
注解的作用
生成文檔
代碼分析,通過代碼里的元數(shù)據(jù)來對代碼進行分析
在編譯的時候進行檢查
JDK自帶的注解
在開發(fā)過程中,我們見到的JDK自帶的注解主要有三個,分別是@Override,@SuppressWarnings,@Deprecated,作用分別如下:
@Override:主要用于標(biāo)識所用的方法是繼承自父類的方法,從而在編譯的過程中可以被編譯器檢測到,如果某個方法使用了該注解,但是方法的簽名(方法名以及參數(shù)類型)與父類不一致,則在編譯的過程中,編譯器會拋出錯誤,用于提示開發(fā)者。
最常見的就是
toString()方法了,該方法繼承自O(shè)bject類。如果我們在重寫該方法的時候,沒有使用該注解,然后不小心將該方法的名字寫錯如下所示:
package cn.xuhuanfeng.annotation;
public class TestAnnotation {
public static void main(String[] args) {
TestAnnotation test = new TestAnnotation();
System.out.println(test.toString());
}
public String tostring() {
return "Not true";
}
}
輸出結(jié)果為:cn.xuhuanfeng.annotation.TestAnnotation@15db9742,顯然我們可以看到,toString()方法輸出的結(jié)果顯然不是我們所期待的,有時候就會很莫名其妙了(原因是重寫toString() 方法的時候,不小心將S寫錯成了s,編譯器會理解成有一個新的方法,叫tostring(),這是正確的,所以就導(dǎo)致了調(diào)用的時候出現(xiàn)了預(yù)期之外的結(jié)果了),但是如果我們在重寫toString()方法的時候,加上@Override,這個時候,如果還是按照上面的寫法,編譯器就會告訴我們The method tostring() of type TestAnnotation must override or implement a supertype method,于是,我們很容易就能發(fā)現(xiàn)問題所在了,這是注解的好處之一,也是@Override 的作用,具體可以查看其源碼即可。
@Deprecated:主要用于標(biāo)識該包、方法、域、變量等已經(jīng)不推薦使用了,一旦標(biāo)識了該注解,則對應(yīng)的方法、域等會劃上刪除線如下所示
@Deprecated
public void test(){
System.out.println("Deprecated");
}
@SuppressWarnings:主要用于壓制編譯器發(fā)出的警告,該注解需要提供參數(shù),包括了
uncheckedall等,分別對應(yīng)不同的壓制范圍,如:
@SuppressWarnings("all")
public void test01(){
}
自定義注解
上面我們看到了JDK中自帶的注解,雖然很有用,但是畢竟范圍有限,種類也有限,實用性不是很大,于是Java開發(fā)者為我們提供了自定義的注解,極大了擴展了該功能,下面我們就詳細來看下自定義注解的內(nèi)容。
元注解
為了使用自定義注解,首先我們需要了解一個概念:元注解,所謂的元注解,其實就是注解的注解,也就是用來表示注解的注解,JDK中包含的元注解中比較常用的有以下幾種類型: @Target , @Retention , @Documented , @Inherited ,其中前面兩種在實際開發(fā)過程中用得比較多,所以下面我們著重來介紹這兩種:
@Target:用于表示所標(biāo)識的注解的使用范圍,其值可以是
ElementType.PACKAGE,ElementType.CONSTRUCTOR,ElementType.METHOD,ElementType.FIELD等,分別對應(yīng)的標(biāo)識對象為 包,構(gòu)造器,方法,域變量,也就是說,只有包含了該范圍,我們定義出來的注解才能用于對應(yīng)的域,多種類型可以組合使用,只需要使用{}包括起即可。具體使用如下:
@Target({ElementType.PACKAGE,ElementType.CONSTRUCTOR ,ElementType.METHOD, ElementType.FIELD})
@Retention:用于標(biāo)識注解的存活周期,包括了
RetentionPolicy.RUNTIME,RetentionPolicy.CLASS,RetentionPolicy.SOURCE,分別對應(yīng)存活周期為運行時,字節(jié)碼,源文件。運行時:標(biāo)識該注解存在于字節(jié)碼中,并且在運行過程中會被JVM加載,可用于反射操作。
字節(jié)碼:標(biāo)識該注解存在于字節(jié)碼中,但是運行時不被JVM加載,默認(rèn)的形式。
源文件:標(biāo)識該注解只存在源文件中,在編譯過程會被編譯器丟棄。
具體使用如下:
@Retention(RetentionPolicy.RUNTIME)
自定義注解
學(xué)習(xí)完了元注解之后,我們就可以開始手動編寫自定義的注解了。
- 格式:
自定義注解的書寫方式跟普通的Java類的書寫方式接近,只是將class 關(guān)鍵字替換為@interface ,如下:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PACKAGE})
public @interface MyAnnotation {
}
從上面的元注解部分我們可以知道,我們定義的注解MyAnnotation 只能用于注解包,而不能用于注解方法、域等。
- 參數(shù):
從前面的@SuppressWarnings 中我們可以知道,注解還可以帶參數(shù),不過在注解中的參數(shù)類型有點奇怪,如下:
String value();// String為參數(shù)類型 value() 整體為變量
String[] value(); // String[] 為參數(shù)類型 value() 整體為變量
默認(rèn)情況下,如果只有一個參數(shù)類型,我們將變量命名為value(),我們也可以聲明多個參數(shù)
String name();
int age(); // 其他類型依此類推
在注解中的所有參數(shù)均可以指定默認(rèn)的值,如下:
String name() default "";
int age() default -1;
由于在使用注解中我們無法標(biāo)識錯誤的情況,所以一般情況下,會將默認(rèn)類型指定為一個不合理值,用來處理注解時判斷所使用的值是合理還是不合理。
- 使用:
定義完了一個我們的自定義注解之后,接下來我們來看下如何使用它。使用的方式跟JDK自帶的注解的方式基本一致,指定對應(yīng)的鍵值對,key為定義的參數(shù)名字,值為需要傳入的值,如果是數(shù)組類型,則傳入數(shù)組即可。
@MyAnnotation(name="xuhaunfeng",age=23)
public void test(){}
//在MyAnnotation中多增加一個變量為 String[] parents();
@MyAnnotation(name="xuhaunfeng",age=23,parents={"AA","BB"})
public void test(){}
注解的應(yīng)用
看完了上面的內(nèi)容,可能你會覺得如果注解只是上面的用法,感覺上是沒有任何作用的,確實,上面所介紹的內(nèi)容都是注解的格式、定義等,但是沒有涉及到其應(yīng)用,注解配合反射,可以實現(xiàn)很多功能,例如:ORM的實現(xiàn),框架中Annotation的應(yīng)用等,不過目前我還沒有學(xué)習(xí)到這些內(nèi)容,所以在后期學(xué)習(xí)之后將會補上,敬請期待。
參考說明
這篇文章只是我個人學(xué)習(xí)過程中的一些筆記,不帶有任何的商業(yè)目的,在學(xué)習(xí)過程中參考了很多的資料,主要參考深入理解Java:注解(Annotation)自定義注解入門 By竹子,在此對竹子表示感謝。如果本文涉及的一些內(nèi)容有一些版權(quán)爭議,還請與我聯(lián)系。