(十五)Java註解

本文目錄

1、Java註解概述

1.1 註解簡介

1.2 註解的作用

2、Java註解的原理

2.1 元註解

2.2 JDK裏的註解

3、註解實例

4、不同類型的註解

4.1 類註解

4.2 方法註解

4.3 參數註解

4.4 變量註解

5、Java註解相關題目


1、Java註解概述

1.1 註解簡介

Annontation是Java5開始引入的新特徵,中文名稱叫註解。它提供了一種安全的類似註釋的機制,用來將任何的信息或元數據(metadata)與程序元素(類、方法、成員變量等)進行關聯。爲程序的元素(類、方法、成員變量)加上更直觀更明瞭的說明,這些說明信息是與程序的業務邏輯無關,並且供指定的工具或框架使用。Annontation像一種修飾符一樣,應用於包、類型、構造方法、方法、成員變量、參數及本地變量的聲明語句中。

Java註解是附加在代碼中的一些元信息,用於一些工具在編譯、運行時進行解析和使用,起到說明、配置的功能。註解不會也不能影響代碼的實際邏輯,僅僅起到輔助性的作用。包含在 java.lang.annotation 包中。

1.2 註解的作用

(1)生成文檔。這是最常見的,也是java 最早提供的註解。常用的有@param @return 等;

(2)跟蹤代碼依賴性,實現替代配置文件功能。比如Dagger 2依賴注入,未來java開發,將大量註解配置,具有很大用處;

(3)在編譯時進行格式檢查。如@override 放在方法前,如果你這個方法並不是覆蓋了超類方法,則編譯時就能檢查出。

2、Java註解的原理

註解本質是一個繼承了Annotation的特殊接口,其具體實現類是Java運行時生成的動態代理類。而我們通過反射獲取註解時,返回的是Java運行時生成的動態代理對象$Proxy1。通過代理對象調用自定義註解(接口)的方法,會最終調用AnnotationInvocationHandler的invoke方法。該方法會從memberValues這個Map中索引出對應的值。而memberValues的來源是Java常量池。

2.1 元註解

java.lang.annotation提供了四種元註解,專門註解其他的註解(在自定義註解的時候,需要使用到元註解):

  @Documented –註解是否將包含在JavaDoc中
  @Retention –什麼時候使用該註解
  @Target –註解用於什麼地方
  @Inherited – 是否允許子類繼承該註解

 (1)@Retention– 定義該註解的生命週期

  • RetentionPolicy.SOURCE : 在編譯階段丟棄。這些註解在編譯結束之後就不再有任何意義,所以它們不會寫入字節碼。@Override, @SuppressWarnings都屬於這類註解。
  •   RetentionPolicy.CLASS : 在類加載的時候丟棄。在字節碼文件的處理中有用。註解默認使用這種方式
  •   RetentionPolicy.RUNTIME : 始終不會丟棄,運行期也保留該註解,因此可以使用反射機制讀取該註解的信息。我們自定義的註解通常使用這種方式。Target – 表示該註解用於什麼地方。默認值爲任何元素,表示該註解用於什麼地方。可用的ElementType參數包括

(2)Target – 表示該註解用於什麼地方。默認值爲任何元素,表示該註解用於什麼地方。可用的ElementType參數包括:

  ● ElementType.CONSTRUCTOR:用於描述構造器
  ● ElementType.FIELD:成員變量、對象、屬性(包括enum實例)
  ● ElementType.LOCAL_VARIABLE:用於描述局部變量
  ● ElementType.METHOD:用於描述方法
  ● ElementType.PACKAGE:用於描述包
  ● ElementType.PARAMETER:用於描述參數
  ● ElementType.TYPE:用於描述類、接口(包括註解類型) 或enum聲明 

(3)@Documented–一個簡單的Annotations標記註解,表示是否將註解信息添加在java文檔中。

(4)@Inherited – 定義該註釋和子類的關係 @Inherited 元註解是一個標記註解,@Inherited闡述了某個被標註的類型是被繼承的。如果一個使用了@Inherited修飾的annotation類型被用於一個class,則這個annotation將被用於該class的子類。

2.2 JDK裏的註解

(1)@Override 這個註解是爲了檢查此方法是否真的是重寫父類的方法。這時候就不用我們用肉眼去觀察到底是不是重寫了。

(2)@Deprecated 此註解代表過時了,但是如果可以調用到,當然也可以正常使用;但是,此方法有可能在以後的版本升級中會被慢慢的淘汰,可以放在類,變量,方法上面都起作用。

(3)@SuppressWarnings 因爲定義的 name 沒有使用,那麼編譯器就會有警告,這時候使用此註解可以屏蔽掉警告;即任意不想看到的編譯時期的警告都可以用此註解屏蔽掉,但是不推薦,有警告的代碼最好還是處理一下。

(4)@FunctionalInterface 此註解是Java8 提出的函數式接口,接口中只允許有一個抽象方法。加上這個註解之後,類中多一個抽象方法或者少一個抽象方法都會報錯。

3、註解實例

(1)註解處理器

註解處理器纔是使用註解整個流程中最重要的一步了。所有在代碼中出現的註解,它到底起了什麼作用,都是在註解處理器中定義好的。

概念:註解本身並不會對程序的編譯方式產生影響,而是註解處理器起的作用;註解處理器能夠通過在運行時使用反射獲取在程序代碼中的使用的註解信息,從而實現一些額外功能。前提是我們自定義的註解使用的是 RetentionPolicy.RUNTIME 修飾的。這也是我們在開發中使用頻率很高的一種方式。

如何通過在運行時使用反射獲取在程序中的使用的註解信息。如下類註解和方法註解。

//類註解
Class aClass = ApiController.class;
Annotation[] annotations = aClass.getAnnotations();

for(Annotation annotation : annotations) {
    if(annotation instanceof ApiAuthAnnotation) {
        ApiAuthAnnotation apiAuthAnnotation = (ApiAuthAnnotation) annotation;
        System.out.println("name: " + apiAuthAnnotation.name());
        System.out.println("age: " + apiAuthAnnotation.age());
    }
}

//方法註解
Method method = ... //通過反射獲取方法對象
Annotation[] annotations = method.getDeclaredAnnotations();

for(Annotation annotation : annotations) {
    if(annotation instanceof ApiAuthAnnotation) {
        ApiAuthAnnotation apiAuthAnnotation = (ApiAuthAnnotation) annotation;
        System.out.println("name: " + apiAuthAnnotation.name());
        System.out.println("age: " + apiAuthAnnotation.age());
    }
}

4、不同類型的註解

4.1 類註解

你可以在運行期訪問類,方法或者變量的註解信息,下是一個訪問類註解的例子:

 Class aClass = TheClass.class;
Annotation[] annotations = aClass.getAnnotations();

for(Annotation annotation : annotations){
    if(annotation instanceof MyAnnotation){
        MyAnnotation myAnnotation = (MyAnnotation) annotation;
        System.out.println("name: " + myAnnotation.name());
        System.out.println("value: " + myAnnotation.value());
    }
}

你還可以像下面這樣指定訪問一個類的註解:  

Class aClass = TheClass.class;
Annotation annotation = aClass.getAnnotation(MyAnnotation.class);

if(annotation instanceof MyAnnotation){
    MyAnnotation myAnnotation = (MyAnnotation) annotation;
    System.out.println("name: " + myAnnotation.name());
    System.out.println("value: " + myAnnotation.value());
}

4.2 方法註解

下面是一個方法註解的例子:

public class TheClass {
  @MyAnnotation(name="someName",  value = "Hello World")
  public void doSomething(){}
}

 你可以像這樣訪問方法註解:

Method method = ... //獲取方法對象
Annotation[] annotations = method.getDeclaredAnnotations();

for(Annotation annotation : annotations){
    if(annotation instanceof MyAnnotation){
        MyAnnotation myAnnotation = (MyAnnotation) annotation;
        System.out.println("name: " + myAnnotation.name());
        System.out.println("value: " + myAnnotation.value());
    }
}

 你可以像這樣訪問指定的方法註解:

Method method = ... // 獲取方法對象
Annotation annotation = method.getAnnotation(MyAnnotation.class);

if(annotation instanceof MyAnnotation){
    MyAnnotation myAnnotation = (MyAnnotation) annotation;
    System.out.println("name: " + myAnnotation.name());
    System.out.println("value: " + myAnnotation.value());
}

4.3 參數註解

方法參數也可以添加註解,就像下面這樣:

public class TheClass {
  public static void doSomethingElse(
        @MyAnnotation(name="aName", value="aValue") String parameter){
  }
}

你可以通過 Method對象來訪問方法參數註解:

Method method = ... //獲取方法對象
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
Class[] parameterTypes = method.getParameterTypes();

int i=0;
for(Annotation[] annotations : parameterAnnotations){
  Class parameterType = parameterTypes[i++];

  for(Annotation annotation : annotations){
    if(annotation instanceof MyAnnotation){
        MyAnnotation myAnnotation = (MyAnnotation) annotation;
        System.out.println("param: " + parameterType.getName());
        System.out.println("name : " + myAnnotation.name());
        System.out.println("value: " + myAnnotation.value());
    }
  }
}

 需要注意的是 Method.getParameterAnnotations()方法返回一個註解類型的二維數組,每一個方法的參數包含一個註解數組。

4.4 變量註解

下面是一個變量註解的例子:

public class TheClass {

  @MyAnnotation(name="someName",  value = "Hello World")
  public String myField = null;
}

你可以像這樣來訪問變量的註解:

Field field = ... //獲取方法對象</pre>
<pre>Annotation[] annotations = field.getDeclaredAnnotations();

for(Annotation annotation : annotations){
 if(annotation instanceof MyAnnotation){
 MyAnnotation myAnnotation = (MyAnnotation) annotation;
 System.out.println("name: " + myAnnotation.name());
 System.out.println("value: " + myAnnotation.value());
 }
}

你可以像這樣訪問指定的變量註解:  

Field field = ...//獲取方法對象</pre>
<pre>
Annotation annotation = field.getAnnotation(MyAnnotation.class);

if(annotation instanceof MyAnnotation){
 MyAnnotation myAnnotation = (MyAnnotation) annotation;
 System.out.println("name: " + myAnnotation.name());
 System.out.println("value: " + myAnnotation.value());
}

5、Java註解相關題目

(1)什麼是註解?他們的典型用例是什麼?

註解是綁定到程序源代碼元素的元數據,對運行代碼的操作沒有影響。

他們的典型用例是:

  • 編譯器的信息 - 使用註解,編譯器可以檢測錯誤或抑制警告

  • 編譯時和部署時處理 - 軟件工具可以處理註解並生成代碼,配置文件等。

  • 運行時處理 - 可以在運行時檢查註解以自定義程序的行爲

(2)描述標準庫中一些有用的註解。

java.lang和java.lang.annotation包中有幾個註解,更常見的包括但不限於此:

  • @Override -標記方法是否覆蓋超類中聲明的元素。如果它無法正確覆蓋該方法,編譯器將發出錯誤

  • @Deprecated - 表示該元素已棄用且不應使用。如果程序使用標有此批註的方法,類或字段,編譯器將發出警告

  • @SuppressWarnings - 告訴編譯器禁止特定警告。在與泛型出現之前編寫的遺留代碼接口時最常用的

  • @FunctionalInterface - 在Java 8中引入,表明類型聲明是一個功能接口,可以使用Lambda Expression提供其實現

(3)可以從註解方法聲明返回哪些對象類型?

返回類型必須是基本類型,String,Class,Enum或數組類型之一。否則,編譯器將拋出錯誤。

4)哪些程序元素可以註解?

註解可以應用於整個源代碼的多個位置。它們可以應用於類,構造函數和字段的聲明,方法及其參數,局部變量,包括循環和資源變量,甚至包,類型的使用,類實例創建,類型轉換,接口,異常拋出上。

(5)有沒有辦法限制可以應用註解的元素?

有,@ Target註解可用於此目的。如果我們嘗試在不適用的上下文中使用註解,編譯器將發出錯誤。

(6)什麼是元註解?

元註解適用於其他註解的註解。

所有未使用@Target標記或使用它標記但包含ANNOTATION_TYPE常量的註解也是元註解:

(7)下面的代碼會編譯嗎?

@Target({ ElementType.FIELD, ElementType.TYPE, ElementType.FIELD })
public @interface TestAnnotation {
    int[] value() default {};
}

不能。如果在@Target註解中多次出現相同的枚舉常量,那麼這是一個編譯時錯誤。

刪除重複常量將使代碼成功編譯:

@Target({ ElementType.FIELD, ElementType.TYPE})

 

 

附言:

本文整理來源於網絡、博客等資源,僅做個人學習筆記複習所用。

如果對你學習有用,請點贊共同學習!

如有侵權,請聯繫我刪!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章