《重學Java系列》之 註解基礎

不詩意的女程序媛不是好廚師~
轉載請註明出處,From李詩雨—[https://blog.csdn.net/cjm2484836553/article/details/104432728]

按照慣例我們先來看下本篇的思維導圖:
在這裏插入圖片描述

本篇來和大家先學習一下註解基礎,後期再一步一步深入學習。

本篇的主要目的就是先對註解有些基本的瞭解,後期會進行大量的代碼實戰,這樣循序漸進,相信我們就可以很好的掌握註解了。

1.什麼是註解?

【註解的定義】:

Java 註解用於爲 Java 代碼提供元數據。作爲元數據,註解不直接影響你的代碼執行,但也有一些類型的註解實際上可以用於這一目的。Java 註解是從 Java5 開始添加到 Java 的。

【通俗點理解】:

註解即標籤。

如果把代碼想象成一個具有生命的個體,註解就是給這些代碼的某些個體打標籤。

2.自定義註解及使用

  • 註解通過 @interface關鍵字進行定義。
public @interface Test {
}

它的形式跟接口很類似,不過前面多了一個 @ 符號。

上面的代碼就創建了一個名字爲 Test 的註解。你可以簡單理解爲創建了一張名字爲 Test的標籤。

  • 使用註解
@Test
public class TestAnnotation {
}

創建一個類 TestAnnotation,然後在類定義的地方加上 @Test就可以用 Test註解這個類了。

你可以簡單理解爲將 Test 這張標籤貼到 TestAnnotation這個類上面。

3.元註解

3.1元註解的理解

元註解:即註解的註解。

元註解是可以註解到註解上的註解,或者說元註解是一種基本註解,但是它能夠應用到其它的註解上面。

如果難於理解的話,你可以這樣理解。元註解也是一張標籤,但是它是一張特殊的標籤,它的作用和目的就是給其他普通的標籤進行解釋說明的。

3.2 常見的5種元註解。

元註解有 @Retention、@Documented、@Target、@Inherited、@Repeatable 5 種。

下面分別一一來說明:

@Retention — 指定了註解的存活時間

Retention 的英文意爲保留期的意思。當 @Retention 應用到一個註解上的時候,它解釋說明了這個註解的的存活時間。

它的取值如下:

  1. RetentionPolicy.SOURCE 註解只在源碼階段保留,在編譯器進行編譯時它將被丟棄忽視。 ----生命週期最短的註解
  2. RetentionPolicy.CLASS 註解只被保留到編譯進行的時候,它並不會被加載到 JVM 中。
  3. RetentionPolicy.RUNTIME 註解可以保留到程序運行的時候,它會被加載進入到 JVM 中,所以在程序運行時可以獲取到它們

我畫一張圖來幫助大家理解 這三個取值下 註解 所能保留到的時期:

在這裏插入圖片描述

@Target ---- 指定註解用在什麼地方

Target 是目標的意思,@Target 指定了註解運用的地方
你可以這樣理解,當一個註解被 @Target 註解時,這個註解就被限定了運用的場景。
類比到標籤,原本標籤是你想張貼到哪個地方就到哪個地方,但是因爲 @Target 的存在,它張貼的地方就非常具體了,比如只能張貼到方法上、類上、方法參數上等等。@Target 有下面的取值

  1. ElementType.ANNOTATION_TYPE 可以給一個註解進行註解
  2. ElementType.CONSTRUCTOR 可以給構造方法進行註解
  3. ElementType.FIELD 可以給屬性進行註解
  4. ElementType.LOCAL_VARIABLE 可以給局部變量進行註解
  5. ElementType.METHOD 可以給方法進行註解
  6. ElementType.PACKAGE 可以給一個包進行註解
  7. ElementType.PARAMETER 可以給一個方法內的參數進行註解

@Inherited — 允許子類繼承父類的註解。

Inherited 是繼承的意思,但是它並不是說註解本身可以繼承,而是說如果一個超類被 @Inherited 註解過的註解進行註解的話,那麼如果它的子類沒有被任何註解應用的話,那麼這個子類就繼承了超類的註解。

這麼說比較抽象,舉個栗子給大家看看:

現在有一個 美女類Beauty,還有一個它的子類 BeautyChild。

那麼當Beauty類被打上了“美女”的標籤後,她的孩子BeautyChild默認也會擁有“美女標籤”。

(PS:也就是美女的孩子還是美女。)

//1.自定義一個使用了@Inherited的標籤
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface InheritedTest {
    String value();
}
//2.美女類Beauty,並且擁有一個“美女”標籤
@InheritedTest("美女")
public class Beauty {
}
//3.美女類的孩子類BeautyChild,並沒有特地指定任何標籤
class BeautyChild extends Beauty {
}
//4.測試:看從子類中能否取到標籤
public class DemoTest {
    public static void main(String[] args) {
        Class<BeautyChild> beautyChildClass = BeautyChild.class;
        if (beautyChildClass.isAnnotationPresent(InheritedTest.class)) {
         System.out.println(beautyChildClass.getAnnotation(InheritedTest.class).value());
        }
    }
}

結果是:從子類中確實取到了父類所擁有的標籤。
在這裏插入圖片描述

@Repeatable — 註解的值可以同時取多個

Repeatable 是可重複的意思。@Repeatable 是 Java 1.8 才加進來的,所以算是一個新的特性。

什麼樣的註解會多次應用呢?通常是註解的值可以同時取多個。

還是來舉個栗子來方便大家理解吧:

比如說我吧,在工作上,我擁有“程序媛”的標籤;在朋友眼中,我擁有“瘋一樣的女子”的標籤;在陌生人眼中,我又擁有“淑女”的標籤。根據不同的場合,我可以擁有很多不同的標籤,這完全沒有問題。

//1.先聲明一個Persons類用來包含所有的身份
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Persons {
    Person[] value();//由於會有多個身份,所以此處是數組
}
//2.定義一個Person註解,它使用到了@Repeatable。
//@Repeatable括號內的就相當於用來保存該註解內容的容器。
@Repeatable(Persons.class)
public @interface Person {
    String role() default "";
}
//3.聲明一個Shiyu類,給該類加上一些身份。
@Person(role = "程序媛")
@Person(role = "瘋一樣的女子")
@Person(role = "淑女")
public class Shiyu {
}
//4.測試看看能否獲取這多個標籤
public class DemoTest2 {
    public static void main(String[] args) {
        if (Shiyu.class.isAnnotationPresent(Persons.class)) {
            Persons p2 = Shiyu.class.getAnnotation(Persons.class);
            for (Person t : p2.value()) {
                System.out.println(t.role());
            }
        }
    }
}

結果標籤全部可以打印出來,則正確:
在這裏插入圖片描述

【注意事項】:

大家在測試@repeatable的代碼時,別忘了配置gradle哈。要加上下邊這個配置纔可以:
在這裏插入圖片描述

即要加上:

compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}

如果不加的話,編譯時會報錯:
在這裏插入圖片描述

所以,切記這些小細節哈~

@Documented—用到的很少

顧名思義,這個元註解肯定是和文檔有關。它的作用是能夠將註解中的元素包含到 Javadoc 中去。ElementType.TYPE 可以給一個類型進行註解,比如類、接口、枚舉

4.註解的屬性

註解的屬性也叫做成員變量。

註解只有成員變量,沒有方法。

【需要注意】的是:

  • 在註解中定義屬性時它的類型是有要求的。

    只能用8種基本數據類型( byte,short,char,int,long,float,double,boolean),外加 類(Class),接口(interface),註解(annotation), String,以及這一些類的數組。

  • 註解中屬性可以有默認值,默認值需要用 default 關鍵值指定

    如:

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Test{
        int id() default -1;
        String msg() default "Hello";
    }
    

    上面代碼定義了 TestAnnotation 這個註解中擁有 id 和 msg 兩個屬性。

    在使用的時候,我們應該給它們進行賦值。

    賦值的方式是在註解的括號內以 value="" 形式,多個屬性之前用 ,隔開:

    @Test(id=1,msg="hello annotation")
    public class TestAnnotation {
    }
    

5.註解的提取

1.運行期註解(RunTime)—利用反射去獲取信息。
2.編譯期(Compile time)註解,以及處理編譯期註解的手段是APT和Javapoet,@Retention(RetentionPolicy.CLASS)。

今天我們主要講一下運行期註解的提取:

首先可以通過 Class 對象的 isAnnotationPresent() 方法判斷它是否應用了某個註解

public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {}

然後通過 getAnnotation() 方法來獲取 Annotation 對象。

 public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}

或者是 getAnnotations() 方法。

public Annotation[] getAnnotations() {}

前一種方法返回指定類型的註解,後一種方法返回註解到這個元素上的所有註解。

如果獲取到的 Annotation 如果不爲 null,則就可以調用它們的屬性方法了。比如

public class TestDemo {
    public static void main(String[] args) {
        boolean hasAnnotation = TestDemo.class.isAnnotationPresent(Test.class);
        if (hasAnnotation) {
            TestAnnotation testAnnotation = TestDemo.class.getAnnotation(Test.class);
            System.out.println("id:" + testAnnotation.id());
            System.out.println("msg:" + testAnnotation.msg());
        }
    }
}

6.註解的使用場景

  • 提供信息給編譯器: 編譯器可以利用註解來探測錯誤和警告信息
  • 編譯階段時的處理: 軟件工具可以用來利用註解信息來生成代碼、Html文檔或者做其它相應處理。
  • 運行時的處理: 某些註解可以在程序運行的時候接受代碼的提取
    值得注意的是,註解不是代碼本身的一部分。

積累點滴,做好自己~

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