Java必知必會之註解

Java團長_發表於2018-10-03

引子:全中國的Java程式設計師都知道,現在主流的專案架構都是ssm、ssh、springcloud等,而這些框架都離不開spring,而spring中使用了大量的註解(包括spring自定義的註解)。因此想要會用註解,我們就得知道Java註解的原理和基本用法,這樣有助於我們在專案中如魚得水。

在JDK5.0中,新增了很多對現在影響很大的特性,如:列舉、自動裝箱和拆箱、註解、泛型等等。其中註解的引入,是為了增加對後設資料的支援。

註解:是一個介面,程式可以通過反射來獲取指定程式元素的Annotation物件,然後通過Annotation物件來取得註解裡的後設資料,註解能用來為程式元素(包、類、方法、成員變數等)設定後設資料,它不影響程式程式碼的執行。如果希望讓程式中的Annotation在執行時起一定作用,只有通過某種配套的工具對Annotation中的資訊進行訪問和處理,這個工具同城為APT(Annotation program Tool)

JDK5.0除了增加註解特性之外,還提供了5個基本的Annotation:

  1. @Overrride:限定重寫父類方法(旨在強制性提醒)

  2. @Deprecated:表示某個程式元素(類、方法)已過時

  3. @SuppressWarnings:抑制編譯警告

  4. @SafeVarargs:堆汙染警告

  5. @FunctionalInterface:指定某個介面必須為函式式介面
    注:函式式介面是指一個介面中只包含一個抽象方法(可以包含多個預設方法或多個static方法)

以上這幾個註解,在我們的專案經常可見,但是因為他們對程式只是一個強制提醒或者警告作用,並不影響程式的執行,因為我們都沒在意這些註解的作用,而當spring出來之後,大量的註解眼花繚亂,作用各異,但他們都有一個共同作用:
讓我們在編碼過程中簡化了不少重複性的程式碼。
因此我們在瞭解了註解的原理之後,必須要能自定義一些註解並且使用它到專案中去,才能讓我們更好的瞭解和使用它。
而要想自定義註解,
就必須得了解Java提供的幾個元註解
那什麼是元註解呢?
元註解:就是負責註解其它註解的註解

在Java5之後定義了4個標準的元註解,分別是:
 1. @Target
 2. @Retention
 3. @Documented
 4. @Inherited

@Target

target註解用來標識註解所修飾的物件範圍。

它的可用範圍(ElementType的取值範圍)有:

  1. CONSTRUCTOR:用於描述構造器(構造方法)

  2. FIELD:用於描述域(成員變數)

  3. LOCAL_VARIABLE:用於描述區域性變數(區域性變數)

  4. METHOD:用於描述方法(普通方法)

  5. PACKAGE:用於描述包(包定義)

  6. PARAMETER:用於描述引數(如catch等引數)

  7. TYPE:用於描述類、介面(包括註解型別) 或enum宣告

  8. ANNOTATION_TYPE:用於註解

  使用例項:  

1@Target(ElementType.FIELD)
2public @interface TargetTest6{
3}
4@Target({ElementType.TYPE_PARAMETER,ElementType.METHOD})
5public @interface TargetTest7{
6}

@Retention

@Retention定義了該Annotation被保留的時間長短:某些Annotation僅出現在原始碼中,而被編譯器丟棄;而另一些卻被編譯在class檔案中;編譯在class檔案中的Annotation可能會被虛擬機器忽略,而另一些在class被裝載時將被讀取(請注意並不影響class的執行,因為Annotation與class在使用上是被分離的)。使用這個meta-Annotation可以對 Annotation的“生命週期”限制。

它的取值(RetentionPoicy)有:

  1. SOURCE:在原始檔中有效(即原始檔保留),註解只保留在原始碼中,編譯器直接丟棄這種註解。

  2. CLASS:在class檔案中有效(即class保留),編譯器把註解記錄在class檔案中,當Java程式執行時,JVM不能獲取該註解的資訊。

  3. RUNTIME:在執行時有效(即執行時保留),編譯器將把註解記錄在class檔案中,當Java執行時,JVM可以獲取註解的資訊,程式可以通過反射獲取該註解的資訊。

1@Target(ElementType.FIELD)
2@Retention(RetentionPolicy.RUNTIME)
3public @interface TargetTest6{
4}

@Inherited

@Inherited註解指定被它休市的註解將具備繼承性:如果莫個類使用了@XXX註解,則其子類自動被@XXX修飾

1@Target(ElementType.FIELD)
2@Retention(RetentionPolicy.RUNTIME)
3@Inherited
4@interface TargetTest6{
5}
6//所有使用了@TargetTest6註解的類講具備繼承性,
7//也就是它的子類自動帶上@TargetTest6註解

@Documented

@Documented用於指定被該註解修飾的註解類將被javadoc工具提取城文件.

1@Target(ElementType.FIELD)
2@Retention(RetentionPolicy.RUNTIME)
3@Inherited
4@Documented
5@interface TargetTest6{
6}
7//javadoc工具生成的API文件將提取@Documented的使用資訊

以上就是所有的元註解以及他們的作用分析,
有了這些元註解有什麼用呢?
。。。。
當然是為了方便我們在專案中自定義註解咯
那自定義註解怎麼自定義呢?
下面我們來看看介紹如何自定義註解並利用註解完成一些實際的功能


語法:

1類修飾符 @interface 註解名稱{
2 //成員變數,在註解中以無形參的形式存在
3 //其方法名和返回值定義了該成員變的名字和型別
4 String name();
5 int age()
6}

可以看到:註解的語法和介面的定義非常類似,他也一樣具備有作用域,但是它的成員變數的定義是以無形參的方法形式存在,名字定義了成員變數的名字,返回值定義了變數型別。
下面我們來自定義一個註解:

1@Target(ElementType.FIELD)
2@Retention(RetentionPolicy.RUNTIME)
3public @interface AnonTest {
4    int age();
5    String name();
6}

註解@AnonTest在執行時有效,作用域在成員變數上,它有兩個成員變數分別是int型的age和String型的name。
接下來我們就可以使用這個註解了

1public class test {    
2     @AnonTest(age = 0, name = "1")
3     public Integer tes;
4}

這樣就是一個註解的定義和使用了,有人會疑惑說,spring中很多註解都是@xxx,為什麼這個@AnonTest一定要要帶上兩個成員變數呢?
原因很簡單:
註解中的成員變數如果沒有預設值,則在使用註解時必須要給成員變數賦值
但如果成員變數有預設值,那可以直接在定義註解時,賦值上去,這樣在使用時就可以省略不寫

1@Target(ElementType.FIELD)
2@Inherited
3@Retention(RetentionPolicy.RUNTIME)
4public @interface AnonTest {
5    int age() default 1;
6    String name() default "註解辣雞";
7}

這樣在呼叫註解時就可以不賦值

1public class test {
2
3    @AnonTest
4    public Integer tes;

上面看到,我們已經使用了註解,但是我們並沒發現@AnonTest對我們的tes成員變數有任何作用,這是因為註解本身在程式中是不會生效的,而是需要程式來提取資料並且處理註解本應該做的工作。
因此
我們需要知道如何從註解中提取資訊並且做相應的處理。
Java使用Annotation介面來代表程式元素前面的註解,該介面是所有註解的父介面,該介面主要有以下幾個實現類:

  1. Class:類定義

  2. Constructor:構造器定義

  3. Field:類成員變數定義

  4. Method:類的方法定義

  5. Package:類的包定義

AnnotatedElement介面是所有程式元素所實現的介面的父介面,所以程式通過反射獲取了某個類的AnnotatedElement物件之後,程式就可以呼叫該物件的如下幾個方法來訪問註解資訊:

  1. getAnnotation(Class

    annotationClass): 返回改程式元素上存在的、指定型別的註解,如果該型別註解不存在,則返回null。
  2. getAnnotations():返回該程式元素上存在的所有註解。

  3. isAnnotationPresent(Class annotationClass):判斷該程式元素上是否包含指定型別的註解,存在則返回true,否則返回false.

  4. getDeclaredAnnotations():返回直接存在於此元素上的所有註釋。

有了這些方法,我們就可以在類、方法、成員變數等程式元素中獲取到註解資訊
比如:

 1@Component
2@Service
3public class test {
4
5    @AnonTest
6    public Integer tes;
7
8    @Deprecated
9    public void m1(){
10
11    }
12
13    public static void main(String[] args) {
14        try {
15            //獲取所有test類上的註解
16            Annotation[] ar=Class.forName("com.anon.test").getAnnotations();
17            //獲取所有test類上m1方法的註解
18            Annotation[] ar1=Class.forName("com.anon.test").getMethod("m1").getAnnotations();
19            //遍歷所有的註解,檢測是否有我們想要的某個註解 如@Component註解
20            for(Annotation a:ar){
21                System.out.println(a.getClass());
22                if(a instanceof  Component)
23                System.out.println("用了註解:"+a);
24            }
25        } catch (Exception e) {
26            // TODO Auto-generated catch block
27            e.printStackTrace();
28        }
29    }
30}
31
32//輸出:
33//用了註解:@org.springframework.stereotype.Component(value=)
34//class com.sun.proxy.$Proxy4

利用AnnotatedElement提供的方法能獲取到我們想要的註解資訊之後,就可以針對註解做一些特定的行為。

例如,對於所有的註冊使用者資訊,系統需要把名稱和年齡上報到另一個年齡分佈表中,就可以利用註解就可以完成這樣的動作

 1public class test {
2    @AnonTest(name="張小龍",age=25)
3    public Integer tes;
4    public static void main(String[] args) {
5        test.getInfo(test.class);
6    }
7    public static void getInfo(Class<?> clazz) {
8        //獲取目標類的所有成員變數
9        Field[] fields = clazz.getDeclaredFields();
10        for (Field field : fields) {
11            //查詢變數中是否有存在@AnonTest註解
12            if (field.isAnnotationPresent(AnonTest.class)) {
13                //如果存在,則做相應處理 
14                AnonTest anon= field.getAnnotation(AnonTest.class);
15                 System.out.println("名稱是:"+anon.name());
16                 System.out.println("年齡是:"+anon.age());
17                 System.out.println("上報資訊到使用者年齡分佈表中");
18                 //上報資訊到使用者年齡分佈表中
19                 //uploadInfoToLog(anon.name()),anon.age())
20            }
21        }
22    }
23}

輸出:

1名稱是:張小龍
2年齡是:25
3上報資訊到使用者年齡分佈表中

最終程式按照我們的需求執行並且輸出正確的資訊,由此可見,註解對於Java來說是一種補充,他的存在與否對程式來說應該是無影響的,靈活使用註解能使開發的效率更高,而且程式規範性也得到增強。



覺得本文對你有幫助?請分享給更多人

關注「程式設計無界」,提升裝逼技能

640

相關文章