Java必知必會之(三)--注解

引子:全中國的Java程序員都知道,現(xiàn)在主流的項(xiàng)目架構(gòu)都是ssm、ssh、springcloud等,而這些框架都離不開spring,而spring中使用了大量的注解(包括spring自定義的注解)。因此想要會用注解,我們就得知道Java注解的原理和基本用法,這樣有助于我們在項(xiàng)目中如魚得水。

在JDK5.0中,新增了很多對現(xiàn)在影響很大的特性,如:枚舉、自動裝箱和拆箱、注解、泛型等等。其中注解的引入,是為了增加對元數(shù)據(jù)的支持。

注解:是一個(gè)接口,程序可以通過反射來獲取指定程序元素的Annotation對象,然后通過Annotation對象來取得注解里的元數(shù)據(jù),注解能用來為程序元素(包、類、方法、成員變量等)設(shè)置元數(shù)據(jù),它不影響程序代碼的執(zhí)行。如果希望讓程序中的Annotation在運(yùn)行時(shí)起一定作用,只有通過某種配套的工具對Annotation中的信息進(jìn)行訪問和處理,這個(gè)工具同城為APT(Annotation program Tool)

JDK5.0除了增加注解特性之外,還提供了5個(gè)基本的Annotation:

  1. @Overrride:限定重寫父類方法(旨在強(qiáng)制性提醒)
  1. @Deprecated:表示某個(gè)程序元素(類、方法)已過時(shí)
  1. @SuppressWarnings:抑制編譯警告
  1. @SafeVarargs:堆污染警告
  1. @FunctionalInterface:指定某個(gè)接口必須為函數(shù)式接口
    注:函數(shù)式接口是指一個(gè)接口中只包含一個(gè)抽象方法(可以包含多個(gè)默認(rèn)方法或多個(gè)static方法)

以上這幾個(gè)注解,在我們的項(xiàng)目經(jīng)??梢?,但是因?yàn)樗麄儗Τ绦蛑皇且粋€(gè)強(qiáng)制提醒或者警告作用,并不影響程序的執(zhí)行,因?yàn)槲覀兌紱]在意這些注解的作用,而當(dāng)spring出來之后,大量的注解眼花繚亂,作用各異,但他們都有一個(gè)共同作用:
讓我們在編碼過程中簡化了不少重復(fù)性的代碼。
因此我們在了解了注解的原理之后,必須要能自定義一些注解并且使用它到項(xiàng)目中去,才能讓我們更好的了解和使用它。
而要想自定義注解,
就必須得了解Java提供的幾個(gè)元注解
那什么是元注解呢?
元注解:就是負(fù)責(zé)注解其它注解的注解

在Java5之后定義了4個(gè)標(biāo)準(zhǔn)的元注解,分別是:
 1. @Target
 2. @Retention
 3. @Documented
 4. @Inherited

@Target

target注解用來標(biāo)識注解所修飾的對象范圍。

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

  1. CONSTRUCTOR:用于描述構(gòu)造器(構(gòu)造方法)
  1. FIELD:用于描述域(成員變量)
  1. LOCAL_VARIABLE:用于描述局部變量(局部變量)
  1. METHOD:用于描述方法(普通方法)
  1. PACKAGE:用于描述包(包定義)
  1. PARAMETER:用于描述參數(shù)(如catch等參數(shù))
  1. TYPE:用于描述類、接口(包括注解類型) 或enum聲明
  1. ANNOTATION_TYPE:用于注解

使用實(shí)例:

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

@Retention

@Retention定義了該Annotation被保留的時(shí)間長短:某些Annotation僅出現(xiàn)在源代碼中,而被編譯器丟棄;而另一些卻被編譯在class文件中;編譯在class文件中的Annotation可能會被虛擬機(jī)忽略,而另一些在class被裝載時(shí)將被讀取(請注意并不影響class的執(zhí)行,因?yàn)锳nnotation與class在使用上是被分離的)。使用這個(gè)meta-Annotation可以對 Annotation的“生命周期”限制。

它的取值(RetentionPoicy)有:

  1. SOURCE:在源文件中有效(即源文件保留),注解只保留在源代碼中,編譯器直接丟棄這種注解。
  1. CLASS:在class文件中有效(即class保留),編譯器把注解記錄在class文件中,當(dāng)Java程序運(yùn)行時(shí),JVM不能獲取該注解的信息。
  1. RUNTIME:在運(yùn)行時(shí)有效(即運(yùn)行時(shí)保留),編譯器將把注解記錄在class文件中,當(dāng)Java運(yùn)行時(shí),JVM可以獲取注解的信息,程序可以通過反射獲取該注解的信息。
1@Target(ElementType.FIELD)2@Retention(RetentionPolicy.RUNTIME)3public @interface TargetTest6{4}

@Inherited

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

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

@Documented

@Documented用于指定被該注解修飾的注解類將被javadoc工具提取城文檔.

1@Target(ElementType.FIELD)2@Retention(RetentionPolicy.RUNTIME)3@Inherited4@Documented5@interface TargetTest6{6}7//javadoc工具生成的API文檔將提取@Documented的使用信息

以上就是所有的元注解以及他們的作用分析,
有了這些元注解有什么用呢?
。。。。
當(dāng)然是為了方便我們在項(xiàng)目中自定義注解咯
那自定義注解怎么自定義呢?
下面我們來看看介紹如何自定義注解并利用注解完成一些實(shí)際的功能


語法:

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

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

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

注解@AnonTest在運(yùn)行時(shí)有效,作用域在成員變量上,它有兩個(gè)成員變量分別是int型的age和String型的name。
接下來我們就可以使用這個(gè)注解了

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

這樣就是一個(gè)注解的定義和使用了,有人會疑惑說,spring中很多注解都是@xxx,為什么這個(gè)@AnonTest一定要要帶上兩個(gè)成員變量呢?
原因很簡單:
注解中的成員變量如果沒有默認(rèn)值,則在使用注解時(shí)必須要給成員變量賦值
但如果成員變量有默認(rèn)值,那可以直接在定義注解時(shí),賦值上去,這樣在使用時(shí)就可以省略不寫

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

這樣在調(diào)用注解時(shí)就可以不賦值

1public class test {23    @AnonTest4    public Integer tes;

上面看到,我們已經(jīng)使用了注解,但是我們并沒發(fā)現(xiàn)@AnonTest對我們的tes成員變量有任何作用,這是因?yàn)樽⒔獗旧碓诔绦蛑惺遣粫У?,而是需要程序來提取?shù)據(jù)并且處理注解本應(yīng)該做的工作。
因此
我們需要知道如何從注解中提取信息并且做相應(yīng)的處理。
Java使用Annotation接口來代表程序元素前面的注解,該接口是所有注解的父接口,該接口主要有以下幾個(gè)實(shí)現(xiàn)類:

  1. Class:類定義
  1. Constructor:構(gòu)造器定義
  1. Field:類成員變量定義
  1. Method:類的方法定義
  1. Package:類的包定義

AnnotatedElement接口是所有程序元素所實(shí)現(xiàn)的接口的父接口,所以程序通過反射獲取了某個(gè)類的AnnotatedElement對象之后,程序就可以調(diào)用該對象的如下幾個(gè)方法來訪問注解信息:

  1. getAnnotation(Class

    <t style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; word-wrap: break-word !important; font-size: inherit; color: inherit; line-height: inherit;">annotationClass): 返回改程序元素上存在的、指定類型的注解,如果該類型注解不存在,則返回null。</t>

  2. getAnnotations():返回該程序元素上存在的所有注解。

  1. isAnnotationPresent(Class annotationClass):判斷該程序元素上是否包含指定類型的注解,存在則返回true,否則返回false.
  1. getDeclaredAnnotations():返回直接存在于此元素上的所有注釋。

有了這些方法,我們就可以在類、方法、成員變量等程序元素中獲取到注解信息
比如:

 1@Component 2@Service 3public class test { 4 5    @AnonTest 6    public Integer tes; 7 8    @Deprecated 9    public void m1(){1011    }1213    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            //遍歷所有的注解,檢測是否有我們想要的某個(gè)注解 如@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 block27            e.printStackTrace();28        }29    }30}3132//輸出:33//用了注解:@org.springframework.stereotype.Component(value=)34//class com.sun.proxy.$Proxy4

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

例如,對于所有的注冊用戶信息,系統(tǒng)需要把名稱和年齡上報(bào)到另一個(gè)年齡分布表中,就可以利用注解就可以完成這樣的動作

 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        //獲取目標(biāo)類的所有成員變量 9        Field[] fields = clazz.getDeclaredFields();10        for (Field field : fields) {11            //查找變量中是否有存在@AnonTest注解12            if (field.isAnnotationPresent(AnonTest.class)) {13                //如果存在,則做相應(yīng)處理 14                AnonTest anon= field.getAnnotation(AnonTest.class);15                 System.out.println("名稱是:"+anon.name());16                 System.out.println("年齡是:"+anon.age());17                 System.out.println("上報(bào)信息到用戶年齡分布表中");18                 //上報(bào)信息到用戶年齡分布表中19                 //uploadInfoToLog(anon.name()),anon.age())20            }21        }22    }23}

輸出:

1名稱是:張小龍2年齡是:253上報(bào)信息到用戶年齡分布表中

最終程序按照我們的需求運(yùn)行并且輸出正確的信息,由此可見,注解對于Java來說是一種補(bǔ)充,他的存在與否對程序來說應(yīng)該是無影響的,靈活使用注解能使開發(fā)的效率更高,而且程序規(guī)范性也得到增強(qiáng)。

強(qiáng)烈推薦

1、Redis模糊查詢在生產(chǎn)環(huán)境出現(xiàn)嚴(yán)重性能問題

2、Linux性能的重要指標(biāo):打開文件數(shù)的限制

3、以Vue為例,解釋JavaScript的反應(yīng)性

4、Java必知必會之----Enum枚舉類揭秘

5、18位身份證號藏什么玄機(jī)?用js教你校驗(yàn)


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

關(guān)注「編程無界」,提升裝逼技能

image
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 01.是否有堆積了很久沒穿過的衣服,因?yàn)閮?yōu)惠買的不常用的小物件,像這些的話我們都可以整理出來,然后處理掉。 02....
    九悠閱讀 169評論 0 3
  • 姓名:母光艷 公司:寧波貞觀電器 【日精進(jìn)打卡第12天】 【知-學(xué)習(xí)】 背誦《六項(xiàng)精進(jìn)》1遍,共56遍, 誦讀《大...
    母光焱閱讀 132評論 0 1

友情鏈接更多精彩內(nèi)容