1.什麼是註解
註解,可以看作是對 一個 類/方法 的一個擴展的模版,每個 類/方法 按照註解類中的規則,來為 類/方法 註解不同的參數,在用到的地方可以得到不同的 類/方法 中註解的各種參數與值
註解也就是Annotation,相信不少人也和我之前一樣以為和注釋和doc一樣,是一段輔助性的文字,其實註解不是這樣的。
從JDK5開始,java增加了對元數據(描述數據屬性的信息)的支持。其實說白就是代碼里的特殊標誌,這些標誌可以在編譯,類載入,運行時被讀取,並執行相應的處理,以便於其他工具補充信息或者進行部署。
2.基本的Annotation
java提供了5個基本的註解,分別是
1.@Override
2.@Deprecated
3.@SuppressWarnings
4.@SafeVarargs
5.@FunctionalInterface
1.限定父類重寫方法:@Override
當子類重寫父類方法時,子類可以加上這個註解,那這有什麼什麼用?這可以確保子類確實重寫了父類的方法,避免出現低級錯誤
2.標示已過時:@Deprecated
這個註解用於表示某個程式元素類,方法等已過時,當其他程式使用已過時的類,方法時編譯器會給出警告(刪除線,這個見了不少了吧)。
3.抑制編譯器警告:@SuppressWarnings
被該註解修飾的元素以及該元素的所有子元素取消顯示編譯器警告,例如修飾一個類,那他的欄位,方法都是顯示警告
4.“堆污染”警告與@SafeVarargs
想理解這個就要明白什麼是堆污染,堆污染是什麼?
其實很好理解,就是把不帶泛型的對象賦給一個帶泛型的對象,為什麼不行?很簡單,因為不帶泛型的話,默認會給泛型設定為object,意思就是什麼類型都可以往裡面塞,那你一個不帶泛型的怎么可能給一個帶泛型塞呢。
例如運行如下代碼:
List list = new ArrayList(); list.add(20); List<String> ls = list; System.out.println(ls.get(0));則會拋出堆污染異常Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String at Test.Test1.main(Test1.java:29)
注意:可變參數更容易引發堆污染異常,因為java不允許創建泛型數組,可變參數恰恰是數組。
抑制這個警告的方法有三個:
1.@SafeVarargs修飾引發該警告的方法或構造器
2.使用@suppressWarnings("unchecked")
3.編譯時使用-Xlint:varargs
5.函式式接口與@Functionallnterface
什麼是函式式?如果接口中只有一個抽象方法(可以包含多個默認方法或多個static方法)
接口體內只能聲明常量欄位和抽象方法,並且被隱式聲明為public,static,final。
接口裡面不能有私有的方法或變數。
這個註解有什麼用?這個註解保證這個接口只有一個抽象方法,注意這個只能修飾接口
3.自定義註解
定義註解非常的簡單,如下
public @interface Test{}
然後這個註解就可以用在別的地方
例如:
@Test
public class MyClass{}
這個自定義註解可以修飾程式中的類、方法、變數、接口等。通常放在所有修飾符之前。
你還可以為自己的註解類添加成員變數
例如:
public @interface MyTag{
//註解中的成員變數以方法的方式來定義
String name();
int age
}
然後
public class Test{
//當使用自定義註解的時候,這個註解本身是不會生效的,必須由開發者工具提取出來並進行處理。
@MyTag(name = "XX" , age = 6)
public void info(){}
}
你甚至還可以為成員變數指定初始值
public @interface MyTag{
String name() default "yeeku"
int age() default 32;
}
當你指定完默認值的時候,你可以直接@MyTag使用不需要帶參數。
4.JDK的元Annotation
1.使用@Retention
這個註解是用來修飾註解定義的,作用是被修飾的註解可以保存多久,這個註解需要使用參數。
這個參數的類型是RetentionPolicy,所以使用這個註解就要對value賦值。
value的值有且僅有三個:
->RetenionPolicy.CLASS 編譯器把該註解記錄在class檔案中。當運行java程式時,JVM不可獲取註解信息。這是默認值!
->RetenionPolicy.RUNTIME編譯器把該註解記錄在class檔案中。當運行java程式時,JVM可獲取註解信息,程式可以通過反射獲取該註解信息
->RetenionPolicy.SOURCE 該註解只保存在原始碼中,編譯器直接丟棄該註解
例如:@Retention(value =RetenionPolicy.SOURCE) 可簡寫為@Retention(RetenionPolicy.SOURCE)
2.使用@Target
@Target也只能修飾一個註解定義,作用是指定被修飾的註解能用於修飾哪些程式單元,@Target也包含了一個value值,他的值只能是下面的:ElementType.
取值 | 註解使用範圍 |
METHOD | 可用於方法上 |
TYPE | 可用於類或者接口上 |
ANNOTATION_TYPE | 可用於註解類型上(被@interface修飾的類型) |
CONSTRUCTOR | 可用於構造方法上 |
FIELD | 可用於域上 |
LOCAL_VARIABLE | 可用於局部變數上 |
PACKAGE | 用於記錄java檔案的package信息 |
PARAMETER | 可用於參數上 |
例如:
@Target(ElementType.METHOD) (這是簡寫)
public @interface Action()
上面就是他的用法,不過有個比較容易混淆的地方就是@interface和interface不是同一個東西:
@interface 不是interface,是註解類 是jdk1.5之後加入的,java沒有給它新的關鍵字
,所以就用@interface 這么個東西表示了 這個註解類,就是定義一個可用的註解
,包括這個註解用於什麼地方,是類,還是方法,還是property,還是方法入參等等
,還有這個註解是否編譯後進入class 比如我們知道的用於javadoc的註解,是不進入class檔案的。
然後在後面你就可以用這個註解寫代碼了。總的來說,這就是一個生成javadoc時用到的注釋類
3.使用@Documented
這個註解用於指定被修飾的註解類將被javadoc工具提取成文檔,如果定義註解類時使用了這個註解修飾,則所有使用該註解修飾的程式設計師蘇API文檔將會包含該註解說明。
例如:@Documentedpublic @interface Testable{}
4.使用@Inherited
這個註解指定被他修飾的註解將具有繼承性——如果某個類使用了@Xxx,則其子類將自動被@Xxx修飾
5.使用@Result
作用是在同一個程式元素前使用多個相同類型的註解在java8之前只能通過@Results配置,
java8簡化了它的寫法例如:
@test(age=5)@test(age=8)public void resultTest(){}
6.使用類型註解
創建類實例new@InternedMyObject();
類型映射myString = (@NonNullString) str;
implements 語句中class UnmodifiableList<T> implements@ReadonlyList<@Readonly T> { ... }
throw exception聲明void monitorTemperature() throws@CriticalTemperatureException { ... }
等等地方都可以用類型註解
新增ElementType.TYPE_USE和ElementType.TYPE_PARAMETER(在Target上)新增的兩個注釋的程式元素類型 ElementType.TYPE_USE 和 ElementType.TYPE_PARAMETER用來描述註解的新場合。ElementType.TYPE_PARAMETER 表示該註解能寫在類型變數的聲明語句中。
ElementType.TYPE_USE 表示該註解能寫在使用類型的任何語句中(eg:聲明語句、泛型和強制轉換語句中的類型)。還是那句話,這些註解本身是沒有意義的。要靠自己實現類型註解的檢查框架,或者第三方,類型註解最大的意義在於,讓編譯器執行更嚴格的檢查,保證代碼更加的健壯。