@Override
- 是告訴編譯器檢查這個方法,保證父類要包含一個被該方法重寫的方法,否則編譯出錯。
- 只能修飾方法,不能修飾其他程式元素 。
Java 9 增強的@Deprecated
- 表示某個程式元素已經過時。
-
Java 9 為@Deprecated註解增加了如下兩個屬性
- ForRemoval:該boolean型別的屬性指定該API在將來是否會被刪除。
- Since:該String型別的屬性指定該API從哪個版本被標記為過時。
public class DeprecatedS {
//已過時,since是從9版本開始,forRemoval指定該API將來會被刪除
@Deprecated(forRemoval=true,since="9")
public void info() {
}
public static void main(String[] args) {
DeprecatedS s = new DeprecatedS();
s.info();
}
}
抑制編譯器警告:@SuppressWarnings
- 指示被該註解修飾的程式元素,以及該程式元素中的所有子元素,取消顯示指定的編譯器警告。@SuppressWarnings會一直作用於該程式元素的所有子元素。
//關閉整個類裡的編譯器警告
@SuppressWarnings(value="unchecked")
public class SuppressWarningsS {
public static void main(String[] args) {
List<String> list = new ArrayList();
}
}
堆汙染警告 與 java 9 增強的@SafeVarargs
- 當把一個不帶泛型的物件賦給一個帶泛型的變數時,往往就會發生堆汙染
public class SafeVarargsS {
public static void main(String[] args) {
}
//Type safety: Potential heap pollution via varargs parameter list
//型別安全:通過varargs引數列表的潛在堆汙染。
public static void ai(List<String> ...list) {
List[] list2 = list;
List<Integer> myList = new ArrayList<>();
myList.add(new Random().nextInt(100));
list2[0] = myList;
String string = list[0].get(0);
}
}
- 上面程式中的粗體字程式碼已經發生了堆汙染,由於該方法有個形參是List<String>…型別,個數可變的形參相當於陣列,但java又不會支援泛型陣列,因此程式只能把List<String>…當成List[]處理,這裡發生了堆汙染。
- 使用@SafeVarargs
@SafeVarargs
public static void ai(List<String> ...list) {}
Java 8的函式式介面 @FuncationInterface
- 如果介面中只有一個抽象方法,可以包含多個預設方法或多個static方法,該介面就是函式式介面。
- @FuncationInterface就是用來指定某個介面必須是函式式介面。
@FunctionalInterface
public interface Function<T, R> {}
- FuncationInterface只能修飾介面,不能修飾其他元素
JDK的元註解
- 在java.lang.annotation包下提供了6個Meta註解(元註解),@Repeatable專門用於定義java 8新增的重複註解。
-
使用@Retention :保留,扣留 Policy:政策,方針
- @Retention只能用於修飾註解定義,用於指定被修飾的註解可以保留多長時間,他包含一個RetentionPolicy型別的value成員變數,所以使用時必須為該value成員變數指定值。
-
Value成員變數的值只能是如下三個
- RetentionPolicy.CLASS : 編譯器將把註解記錄在class中,當執行java程式時,JVM不可獲得註解資訊,這是預設值。
- RetentionPolicy.REUNTIME:編譯器將把註解記錄在class檔案中,當執行java程式時,JVM也可獲取註解資訊,程式可以通過反射獲得該註解資訊。
- RetentionPolicy.SOURCE:註解只保留在原始碼中,編譯器直接丟棄這種註解。
- 使用
@Retention(value = RetentionPolicy.RUNTIME)
public @interface My1 {}
@Retention(RetentionPolicy.RUNTIME)
public @interface My1 {}
-
使用@Target
- 只能修飾註解定義,它用於指定被修飾的註解能用於修飾那些程式單元。
- @Target元註解也包含一個value成員變數
-
成員變數如下
- ElementType.ANNOTATION_TYPE:指定該策略的註解只能修飾註解。
- ElementType.CONSTRUCTOR:指定該策略的註解只能修飾構造器。
- ElementType.FIELD:指定該策略的註解只能修飾成員變數。
- ElementType.LOCAL_VARIABLE:指定該策略的註解只能修飾區域性變數。
- ElementType.METHOD:指定該策略的註解只能修飾方法。
- ElementType.PACKAGE:指定該策略的註解只能修飾包定義。
- ElementType.PARAMETER:指定該策略的註解只能修飾引數。
- ElementType.TYPE:指定該策略的註解只能修飾類,介面,註解型別,列舉定義。
- 使用
@Target(value = ElementType.ANNOTATION_TYPE)
public @interface My1 {}
-
使用@Document
- @Document用於指定被該元註解修飾的註解類將被javadoc工具提取城文件,如果定義註解類時使用了@Document,則所有使用該註解修飾的程式元素的API文件中將會包含該註解說明。
-
使用@Inherited : 可繼承的
- 指定被他修飾的註解將具有繼承性。
@Inherited
public @interface My1 {}
- 上面程式中程式碼表明@My1具有繼承性,如果某個類使用@My1修飾,則該類的子類將自動使用@My1修飾。
- 檢查其子類是否預設使用@My1修飾。
@My1
class A{}
public class My1_Test extends A {
public static void main(String[] args) {
System.out.println(My1_Test.class.isAnnotationPresent(My1.class));//true
}
}
自定義註解
- 定義新的註解使用@Interface關鍵字定義一個新的註解型別與定義一個介面非常像,如下
public @interface My1 {}
- 定義了該註解之後,就可以在程式的任何地方使用該註解。
- 預設情況下,註解可用於修飾任何程式元素,包括類,介面,方法等。
- 註解還可以帶成員變數,成員變數在註解定義中以無形參的方法形式來宣告,其方法名和返回值定義了該成員變數的名字和型別。
public @interface My1 {
String name();
int id();
}
- 一旦在註解裡定義了成員變數後,使用該註解時就應該為他的成員變數指定值。
@My1(id=1,name="rrr")
class A{}
(6) 也可以在定義註解的成員變數時為其指定初始值,指定預設值default。
public @interface My1 {
String name() default "ccc";
int id() default 123;
}
- 成員變數指定了值,則預設值就不會起作用。
-
根據註解是否可以包含成員變數,可以把註解分為
- 標記註解:沒有定義成員變數的註解型別被稱為標記。這種註解僅利用自身的存在與否來提供資訊,如@Test、
- 後設資料註解:包括成員變數的註解,因為他們可以接受更多的後設資料,所以也被稱為後設資料註解。
提取註解資訊
- 使用註解修飾了類,方法,成員變數等之後,這些註解不會自己生效,必須由開發者提供相應的工具來提取並處理註解資訊。
-
AnnotatedElement介面,該介面代表程式中可以接受註解的程式元素,該介面主要有如下個實現類:
- Class:類定義
- Constructor:構造器定義
- Field:類的成員變數定義
- Method:類的方法定義
- Package:類的包定義
- 只有當定義註解時使用了@Retention(RetentionPolicy.RUNTIME)修飾,該註解才會在執行時可見,JVM才會在裝載class檔案時讀取儲存在class檔案中的註解資訊。
- 獲取My2類的方法上的Annotation
public class My2 {
@My1(id =123,name ="dsds")
public void info() {
}
}
public class GetMy2Annotation{
public static void main(String[] args) throws Exception{
Class<?> forName = Class.forName("annotations.My2");//載入類
Method method = forName.getMethod("info");//得到方法
Annotation[] annotations = method.getAnnotations();//得到方法上所有註解
//@annotations.My1(name="dsds", id=123)
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
}
}
- 訪問註解中的後設資料
public class GetMy2Annotation{
public static void main(String[] args) throws Exception{
Class<?> forName = Class.forName("annotations.My2");//載入類
Method method = forName.getMethod("info");//得到方法
Annotation[] annotations = method.getAnnotations();//得到方法上所有註解
/*
* 123
* dsds
* */
for (Annotation annotation : annotations) {
if (annotation instanceof My1) {
System.out.println(((My1)annotation).id());
System.out.println(((My1)annotation).name());
}
}
}
}
- 利用註解模擬@Test的JUnit效果
public class RunTest {
public static void main(String[] args) throws Exception {
Class<?> forName = Class.forName("annotations.RunTest_JUnit");//載入類
Method[] methods = forName.getDeclaredMethods();//獲得本類所有方法
int success = 0;//成功方法
int fail = 0;//失敗方法
for (Method method : methods) {
System.out.println(method);
System.out.println(method.isAnnotationPresent(Testable.class));
if (method.isAnnotationPresent(Testable.class)) {
try {
//抑制private訪問修飾符
method.setAccessible(true);
method.invoke( null);
success++;
} catch (Exception e) {
fail++;
}
}
}
System.out.println("成功方法有:" + success +"個 失敗的有:"+fail+"個");
}
}
class RunTest_JUnit{
@Testable
private static void t() {
System.out.println("=========================t");
}
private static void t1() {
}
@Testable
private static void t2() {
System.out.println("=========================t2");
}
@Testable
private static void r2() {
System.out.println("=========================r2");
throw new RuntimeException("出錯誤啦");
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Testable{
}
Output:
private static void annotations.RunTest_JUnit.t1()
false
private static void annotations.RunTest_JUnit.t()
true
=========================t
private static void annotations.RunTest_JUnit.r2()
true
=========================r2
private static void annotations.RunTest_JUnit.t2()
true
=========================t2
成功方法有:2個 失敗的有:1個
- 上面的@Testable用於標記那些方法是可測試的,該註解可以作為JUnit測試框架的補充,在JUnit框架中他要求測試用例的測試方法必須以test開頭。
Java 8 新增的重複註解
@Retention(RetentionPolicy.RUNTIME)
@Target(value=ElementType.TYPE)
@Repeatable(value=FkTags.class)//只能可以包容它的容器類
public @interface FkTag {
String name() default "王自強";
int id();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(value=ElementType.TYPE)
public @interface FkTags {
FkTag[] value();
}
//@FkTags({@FkTag(id=1,name="dsd"),@FkTag(id=213)})
@FkTag(id=123)
@FkTag(id=1233)
public class Test_FkTage {
public static void main(String[] args) {
Class<Test_FkTage> cl = Test_FkTage.class;//獲取類
//這個方法可以獲得多個重複註解,而getDeclaredAnnotation只能獲取一個
FkTag[] annotationsByType = cl.getAnnotationsByType(FkTag.class);
for (FkTag fkTag : annotationsByType) {
System.out.println(fkTag.id()+" "+ fkTag.name());
}
FkTags annotation = cl.getAnnotation(FkTags.class);
System.out.println(annotation);
}
}
Output
123 王自強
1233 王自強
@fkAnnotation.FkTags(value={@fkAnnotation.FkTag(name="王自強", id=123), @fkAnnotation.FkTag(name="王自強", id=1233)})
- 如上的重複註解只是一種簡便的寫法,運用@Repeatable註解來制定他的容器註解類即可。
- 容器註解類註解的保留期必須比他所包含的註解的保留期更長,否則編譯器報錯。
Java 8新增的型別註解
編寫自定義註解時未寫@Inherited的執行結果 | 編寫自定義註解時寫了@Inherited的執行結果 | |
---|---|---|
子類的類上能否繼承到父類的類上的註解? | 否 | 能 |
子類方法,實現了父類上的抽象方法,這個方法能否繼承到註解? | 否 | 否 |
子類方法,實現了父類上的方法,這個方法能否繼承到註解? | 能 | 能 |
子類方法,覆蓋了父類上的方法,這個方法能否繼承到註解? | 否 | 否 |