天天看點

Java學習筆記 16、注解

文章目錄

  • ​​前言​​
  • ​​一、認識注解​​
  • ​​1.1、介紹注解​​
  • ​​1.2、開發中常見注解使用​​
  • ​​二、自定義注解​​
  • ​​2.1、自定義注解說明​​
  • ​​2.2、JDK1.5提供的四個元注解​​
  • ​​三、利用反射擷取注解資訊​​
  • ​​四、JDK8中注解的新特性​​
  • ​​4.1、可重複注解(兩種方式)​​
  • ​​兩種方式實作​​
  • ​​擷取可重複注解的值(兩種方式)​​
  • ​​4.2、新增類型注解(2個)​​
  • ​​參考文章​​

前言

      本篇文章是對之前學習java基礎知識的再整理,通過看視訊以及查閱部落格進行彙總整理。部分圖引用的是尚矽谷的教案,覺得比較好,若有侵權,請聯系我删除。

      部落格文章彙總:部落格目錄索引(持續更新)

一、認識注解

1.1、介紹注解

​注解​

​​(Annotation):從​

​JDK5.0​

​開始,Java增加了對中繼資料(MetaData)的支援,及注解。

​Annotation​

​就是代碼中的特殊标記,這些标記可以通過設定在編譯時、類加載時、運作時讀取來執行相應的處理。通過注解可以在不改變原有邏輯的情況下,在源檔案中嵌入一些補充資訊。并且代碼分析工具、開發工具和部署工具都可以通過這些補充資訊進行驗證部署。

  • ​Annoation​

    ​可以像修飾符一樣使用,可用于修飾包、類、構造器、方法…之前,并且能夠設定指定的值儲存在Annoation中的屬性裡,例如name=value。

目的:在JavaSE中,注解的使用目的例如标記過時的功能(​

​@Deprecated​

​​), 忽略警告(​

​@SuppressWarnings​

​​;在JavaEE/Android開發中注解占據了更重要的角色,例如​

​Spring​

​​架構中的​

​AOP​

​通過注解來配置任意方法的切面,使用注解來代替JavaEE舊版中的繁冗代碼與XML配置。

  • 未來的開發模式都是基于注解的,例如:​

    ​Spring2.5​

    ​​以上都是基于注解,​

    ​JPA​

    ​​,​

    ​Hibernate3.X​

    ​​,​

    ​Struts2​

    ​。
  • ​架構=注解+反射+設計模式​

1.2、開發中常見注解使用

使用注解前需要在前面加上​

​@​

​符号,可以将其當做一個修飾符來使用。

1、用于生成文檔的相關注解

一般使用于注釋中,用于在使用​

​javadoc​

​工具來生成文檔

  • 類注釋中:
  • ​@author​

    ​​:标明開發該類子產品的作者,多個作者之間可使用​

    ​,​

    ​分割。
  • ​@version​

    ​:标明該類子產品的版本。
  • ​@since​

    ​:從哪個版本開始增加的,我們常看到源代碼注釋中會标注。
  • ​@see​

    ​:參考轉向,也就是相關主題。
  • 一般用于方法中:
  • ​@param​

    ​:用于在方法注釋中,對方法中某參數的說明,如果沒有參數就不能寫。
  • ​@return​

    ​:用于在方法注釋中,對方法傳回值的說明,如果方法的傳回值類型是void就不能寫。
  • ​@exception​

    ​:對方法可能抛出的異常進行說明 ,如果方法沒有用throws顯式抛出的異常就不能寫 其中
2、在編譯時進行格式檢查(JDK内置的三個基本注解)

一般修飾在方法:

  • ​@Override​

    ​: 限定重寫父類方法,該注解隻能用于方法,可以來檢查是否為重寫的方法。
  • ​@Deprecated​

    ​: 用于表示所修飾的元素(類, 方法等)已過時。通常是因為所修飾的結構危險或存在更好的選擇。
  • ​@SuppressWarnings​

    ​: 抑制編譯器警告。
3、跟蹤代碼依賴性,實作替代配置檔案功能

①Servlet3.0中提供的注解,使得不再需要在web.xml中進行Servlet的部署通路。

Java學習筆記 16、注解
  • 通過使用​

    ​@WebServlet("/login")​

    ​來代替掉下面的配置檔案(減少xml配置)。

②​

​Spring​

​中事務的配置,隻用一個注解來替換掉了配置檔案

Java學習筆記 16、注解

二、自定義注解

2.1、自定義注解說明

對于自定義注解的說明:

  1. 定義​

    ​Annotation​

    ​​時需要使用​

    ​@interface​

    ​關鍵字。
  2. 自定義注解自動繼承了​

    ​java.lang.annotation.Annotation​

    ​​接口,包含四個方法​

    ​equals()​

    ​​、​

    ​hashCode()​

    ​​、​

    ​toString()​

    ​​、​

    ​annotationType()​

    ​。
  3. ​Annoation​

    ​中的成員變量以無參數方法的形式來聲明。
  • 其方法名和傳回值定義了該成員的名字和類型,稱之為配置參數。其類型隻能是​

    ​8種基本資料類型​

    ​​、​

    ​String類型​

    ​​、​

    ​Class類型​

    ​​、​

    ​enum類型​

    ​​、​

    ​Annotation類型​

    ​​以及​

    ​上面所有類型的數組​

    ​。
  1. 可以在定義成員變量時指定參數值,可在成員變量後使用​

    ​default 預設值​

    ​來進行初始化。
  2. 若是​

    ​Annoation​

    ​​中隻有一個參數成員,建議使用參數名為​

    ​value​

    ​。

使用說明:若是使用的注解中含有配置參數(即成員變量),那麼使用時需要在()中指定參數值(對于有預設值的可以不寫因為會自動指派),例如:​

​@注解(參數=值)​

​​;若是​

​Annotation​

​​中隻有一個成員那麼我們在使用時可以省略​

​參數=​

​​,​

​例如:@注解(值)​

​。

注意:沒有成員定義的Annotation稱為​

​标記​

​​;包含成員變量的Annotation稱為​

​中繼資料​

​。

自定義注解
public @interface MyAnnotation {
    String value() default "changlu";
}

@MyAnnotation(value = "changlu")  //或 @MyAnnotation("liner")   或  @MyAnnotation() 
class Person{
}      
  • ​@MyAnnotation(value = "changlu")​

    ​​與​

    ​@MyAnnotation("liner")​

    ​都相當于對其中的value重新指派了,由于注解中隻有一個成員是以可以省略參數直接填值。
  • ​@MyAnnotation() ​

    ​:會預設使用注解成員中的值。

2.2、JDK1.5提供的四個元注解

四個标準的元注解:​

​Retention​

​​、​

​Target​

​​、​

​Documented​

​​、​

​Inherited​

@Retention

​@Retention​

​:用來指定該Annotation的生命周期。

@Documented
@Retention(RetentionPolicy.RUNTIME)  //生命周期為運作時
@Target(ElementType.ANNOTATION_TYPE) //隻可标注在注解類型上
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}

//@Retention的成員傳回值RetentionPolicy,一個枚舉類
public enum RetentionPolicy {
    SOURCE,//在源檔案中有效(僅在源檔案中保留),編譯器編譯時會忽略該注解
    CLASS,//僅在class檔案中有效(即class保留),當運作Java程式時,JVM不會保留注解。
    RUNTIME//:在運作時有效(即運作時保留),當運作 Java 程式時, JVM 會保留注釋。程式可以通過反射擷取該注釋。
}      
Java學習筆記 16、注解
  • 該注解使用不同​

    ​RetentionPolicy​

    ​的枚舉執行個體表示的不同生命周期
@Target

​@Target​

​:用于指定被修飾的Annotation能用于修飾哪些程式元素(如方法、類、包、參數…)。

@Documented
@Retention(RetentionPolicy.RUNTIME) //生命周期為運作時
@Target(ElementType.ANNOTATION_TYPE)//隻可标注在注解類型上
public @interface Target {
    
    //是一個數組枚舉類
    ElementType[] value();
}

//@Target的成員傳回值是一個注解
public enum ElementType {
    TYPE,//使用于描述類、接口(包括注解類型)或enum聲明
    FIELD,//使用于屬性(包含注解内容)
    METHOD,//使用于方法
    PARAMETER,//參數
    CONSTRUCTOR,//用于描述構造器
    LOCAL_VARIABLE,//描述局部變量
    ANNOTATION_TYPE,//描述注解類型聲明
    PACKAGE,//包
    TYPE_PARAMETER,//jdk1.8新增,表示該注解能寫在類型變量的聲明語句中(如:泛型聲明)
    TYPE_USE//jdk1.8新增,表示該注解能寫在使用類型的任何語句中
}      
  • ​@Tatget({ElementType.TYPE_USE})​

    ​:即可在任何語句中添加注解。
@Documented

​@Documented​

​: 用于指定被 修飾的 Annotation 類能夠被 javadoc工具提取成文檔。預設情況下,javadoc是不包括注解的。

  • 若定義了​

    ​@Documented​

    ​​的注解務必設定​

    ​@Retention​

    ​​的值為​

    ​Runtime​

    ​(生命周期為運作時)。
@Documented
@Retention(RetentionPolicy.RUNTIME) //生命周期是運作時
@Target(ElementType.ANNOTATION_TYPE) //隻可标注在注解類型上
public @interface Documented {
}      
@Inherited

​@Inherited​

​:被其修飾的注解具有繼承性,若某個類使用了被@Inherited修飾的注解,則其子類将自動具有該注解。

  • 實際使用較少。
@Documented
@Retention(RetentionPolicy.RUNTIME)//生命周期是運作時
@Target(ElementType.ANNOTATION_TYPE)//隻可标注在注解類型上
public @interface Inherited {
}      

三、利用反射擷取注解資訊

自定義注解:

@Documented
@Target({ElementType.ANNOTATION_TYPE,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)//運作時
@Inherited
public @interface MyAnnotation {
    String value() default "changlu";
}      

通過反射擷取到類的注解以及注解值:

@MyAnnotation("clle")
class Person{
}

class Student extends Person{
}

public class Test {
    public static void main(String[] args) {
        Class<Person> clazz = Person.class;
        //擷取該類上指定的注解類型
        MyAnnotation anno = clazz.getAnnotation(MyAnnotation.class);
        System.out.println(anno.value());//clle

        //檢視Student是否繼承了Person的注解
        Annotation[] annotations = Student.class.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);//@xyz.changlu.MyAnnotation(value=clle)
        }
    }
}      
認識一下​

​AnnotatedElement​

​接口

Class實作了該接口,才會有擷取注解的相關方法:

Java學習筆記 16、注解
  • ​isAnnotationPresent()​

    ​:此元素上是否存在指定注解類型,若存在傳回true,不存在傳回false。
  • ​getAnnotation()​

    ​:擷取該元素上指定注解類型的執行個體。
  • ​getAnnotations()​

    ​:擷取該元素上的所有注解執行個體。
  • ​getAnnotationsByType​

    ​:擷取該元素上指定注解類的所有執行個體。

​Class​

​類的繼承圖:

Java學習筆記 16、注解

四、JDK8中注解的新特性

4.1、可重複注解(兩種方式)

兩種方式實作

什麼是可重複注解呢?就是在同一個部分上定義兩個或多個相同的注解,其中的值可不一樣。

①jdk1.8之前,需要通過再定義一個注解,其中包含我們想要使用的注解數組
@Target({ElementType.ANNOTATION_TYPE,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)//運作時
@Inherited
public @interface MyAnnotation {
    String value() default "changlu";
}

//該注解中我們定義@MyAnnotation數組
@Target({ElementType.ANNOTATION_TYPE,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)//運作時
@Inherited
@interface MyAnnotations{
    //定義注解數組
    MyAnnotation[] value();
}      
@MyAnnotations({@MyAnnotation("changlu"),@MyAnnotation("liner")})
class Person{
}      
  • 我們通過定義​

    ​@MyAnnotations​

    ​注解的成員為注解數組形式來實作可重複注解。
②jdk1.8新增​

​@Repeatable​

​注解
@Target({ElementType.ANNOTATION_TYPE,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Repeatable(MyAnnotations.class)//注意:這裡新增使用@Repeatable()其中值為一個Class類就是下面定義的MyAnnotations
public @interface MyAnnotation {
    String value() default "changlu";
}

@Target({ElementType.ANNOTATION_TYPE,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)//運作時
@Inherited
@interface MyAnnotations{
    //定義注解數組
    MyAnnotation[] value();
}      
  • 在​

    ​@MyAnnotation​

    ​​中使用了​

    ​@Repeatable(MyAnnotations.class)​

    ​注解,其中定義了指定一個容器注解。
@MyAnnotation("changlu")
@MyAnnotation("liner")
class Person{
}      

這樣我們就能夠直接在一個類上定義多個相同注解了,相較于第一種方式需要先聲明​

​@MyAnnotations​

​,其中再包裹兩個注解。

我們再看一下​

​@Repeatable​

​注解:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
    
    Class<? extends Annotation> value();//其中成員的傳回值為一個Class類型
}      

擷取可重複注解的值(兩種方式)

擷取上面使用​

​@Repeatable​

​注解方法的設定的重複注解:

@MyAnnotation("changlu")
@MyAnnotation("liner")
class Person{
}      
public class AnnotationTest {
    public static void main(String[] args) {
        //方式一:通過getAnnotation()擷取MyAnnotations容器注解的執行個體,接着周遊
        MyAnnotations annotation = Person.class.getAnnotation(MyAnnotations.class);
        MyAnnotation[] value = annotation.value();
        for (MyAnnotation myAnnotation : value) {
            System.out.println(myAnnotation.value());
        }
        //changlu
        //liner
    }

    //方式二:通過使用getAnnotationsByType()擷取MyAnnotation注解數組,接着重複周遊即可
    @Test
    public void test(){
        MyAnnotation[] annos = Person.class.getAnnotationsByType(MyAnnotation.class);
        for (MyAnnotation anno : annos) {
            System.out.println(anno.value());
        }
        //changlu
        //liner
    }
}      
  • 通過查閱部落格得知使用新增的​

    ​@Repeatable​

    ​,在編譯器編譯時實際上也是轉換為使用的​

    ​MyAnnotations​

    ​注解表示。
@MyAnnotation("changlu")
@MyAnnotation("liner")      
  轉換後
@MyAnnotations(value=[@MyAnnotation("changlu"),@MyAnnotation("liner")])
//并且在位元組碼中數組使用[]來表示      

對于擷取單個注解值使用​

​getAnnotation()​

​​方法,擷取重複注解使用​

​getAnnotationsByType()​

​。

詳細内容可看我下面的參考文章[1]。

4.2、新增類型注解(2個)

也就是在​

​@Target​

​注解中的成員參數​

​ElementType​

​枚舉類中添加了兩個執行個體:

Java學習筆記 16、注解
  • ​ElementType.TYPE_PARAMETER​

    ​ :表示該注解能寫在類型變量的聲明語 句中(如:泛型聲明)。
  • ​ElementType.TYPE_USE​

    ​:表示該注解能寫在使用類型的任何語句中。

差別說明:在​

​Java 8​

​​之前,注解隻能是在聲明的地方所使用,​

​Java8​

​​開始,注解可以應用在任何地方(可直接使用​

​ElementType.TYPE_USE​

​)。

參考文章

[1]. ​​Java 8 可重複注解的了解與應用​​

我是長路,感謝你的閱讀,如有問題請指出,我會聽取建議并進行修正。

歡迎關注我的公衆号:長路Java,其中會包含軟體安裝等其他一些資料,包含一些視訊教程以及學習路徑分享。

學習讨論qq群:891507813 我們可以一起探讨學習

注明:轉載可,需要附帶上文章連結