后端JAVA:说说你对【注解】的理解
本文主要內(nèi)容如下:
背景
現(xiàn)在已經(jīng)處于注解盛行時代,注解@Override ,這個注解是再熟悉不過了,還有@Controller、@RequestMapping、@Service.....
注解已經(jīng)是作為一個開發(fā)中必備的技能了。
如果在面試中被問到注解,說不出個123,就只能回去等通知了。
什么是注解?
注解annotation是JavaSE5.0中新增功能。可以理解為注解是一種標(biāo)記,這種標(biāo)記可以在編譯、類加載、運行時被讀取,并執(zhí)行相應(yīng)的處理。
它可以添加到程序的任何元素上:包聲明、類型聲明、構(gòu)造方法、普通方法、成員變量、參數(shù)。
注解的老大:
????package?java.lang.annotation;//是個接口public?interface?Annotation?{????boolean?equals(Object?obj);int?hashCode();?String?toString();?//獲取注解類型Class<??extends?Annotation>?annotationType();}JDK自帶為我們提供了元注解,下面就來聊聊JDK的元注解。
元注解有哪些?
JDK為我們提供五個元注解,位于java.lang.annotation 包目錄下。分別為:
@Retention
@Target
@Documented
@Inherited
@Repeatable
Retention注解
字面翻譯:
該注解就是定義該注解是作用于什么階段。
編譯、類加載、運行(使用最多)
注解源碼:
????@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.ANNOTATION_TYPE)public?@interface?Retention?{/***?Returns?the?retention?policy.*?@return?the?retention?policy*/RetentionPolicy?value();}保留策略
????package?java.lang.annotation;public?enum?RetentionPolicy?{?SOURCE,?CLASS,?RUNTIME}SOURCE:只在Java源代碼中,編譯器編譯的時候會把它直接丟棄。
CLASS:編譯器將注解記錄在.class文件中。當(dāng)運行Java程序時,JVM不能獲取注解信息。這是默認(rèn)值。
RUNTIME:編譯器將注解記錄在.class文件中。當(dāng)運行Java程序時,JVM也能獲取注解信息。程序可以通過反射獲取注解信息。
注意:如果使用該注解的時候必須指定value的值。
Target注解
字面意義為目標(biāo),下面來看看器源碼:
????@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.ANNOTATION_TYPE)public?@interface?Target?{//元素類型ElementType[]?value();}public?enum?ElementType?{?TYPE,?FIELD,?METHOD,?PARAMETER,?CONSTRUCTOR,?LOCAL_VARIABLE,?ANNOTATION_TYPE,?PACKAGE,?/**?@since?1.8*/TYPE_PARAMETER,/**?@since?1.8*/TYPE_USE}Target注解是在聲明創(chuàng)建一個注解的時候,指示該注解可以作用于程序中的哪些元素。
它里邊也包含一個名為value的成員變量,value成員變量的值有如下幾種
ANNOTATION_TYPE:指定當(dāng)前注解只能修飾其它注解
CONSTRUCTOR:指定當(dāng)前注解只能修飾構(gòu)造方法
FIELD:指定當(dāng)前注解只能修飾成員變量
LOCAL_VARIABLE:指定當(dāng)前注解只能修飾局部變量
METHOD:指定當(dāng)前注解只能修飾方法
PACKAGE:指定當(dāng)前注解只能修飾包
PARAMETER:指定當(dāng)前注解只能修飾參數(shù)
TYPE:指定當(dāng)前注解可以修飾類,接口,其它注解,枚舉等類型
比如說:常用的Spring的注解@Controller,是用來作用于類上的。
Documented 注解
字面意義就是文檔。@Documented用于描述其它類型的annotation應(yīng)該被作為被標(biāo)注的程序成員的公共API,因此可以被例如javadoc此類的工具文? ? 檔化。Documented是一個標(biāo)記注解,沒有成員 。
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Documented { }主要是用來生成文檔的,工作中基本上很少使用,作為了解就可以了。
Inherited 注解
字面意義:
@Inherited注解是在聲明創(chuàng)建一個注解的時候,指定該注解將具有繼承性。
Inherited 源碼:
????@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.ANNOTATION_TYPE)public?@interface?Inherited?{}如果某個類使用了該注解@Xx,則其子類也將自動被此注解@Xx所修飾。
使用模板:
????@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Inheritedpublic?@interface?A?{}@Apublic?class?Base?{}//Sub也帶有@Apublic?class?Sub?extends?Base?{}Repeatable注解
@Repeatable元注解,顧名思義,重復(fù)注解,就是在聲明創(chuàng)建注解的時候,指定該注解可以被同一個程序元素多次使用。這是JDK8新增的注解,重復(fù)注解只是一種簡單化寫法,這種簡單化寫法是一種假象。多個重復(fù)注解其實會被作為“容器”注解的value成員變量的數(shù)組元素。
比如下面Spring中注解ComponentScan:
????@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documented@Repeatable(ComponentScans.class)public?@interface?ComponentScan?{}@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documentedpublic?@interface?ComponentScans?{ComponentScan[]?value();}使用案例
????import?org.springframework.context.annotation.Bean;import?org.springframework.context.annotation.ComponentScan;import?org.springframework.context.annotation.ComponentScans;@ComponentScans(value?=?{@ComponentScan(value?=?"com.tian.pakage0"),@ComponentScan(value?=?"com.tian.pakage1")})public?class?MainConfig?{@Beanpublic?User?person()?{return?new?User();}}基本注解
注解必須使用工具來處理,工具負(fù)責(zé)提取注解中包含的元數(shù)據(jù),工具還會根據(jù)這些元數(shù)據(jù)增加額外的功能。下面我們先來了解一下5個基本注解的用法。
Override
SafeVarargs
SuppressWarnings
FunctionalInterface
Deprecated
下面我們就來說說這五個注解。
Override
用于標(biāo)識方法,標(biāo)識該方法屬于重寫父類的方法 。
????//只能用于方法上@Target(ElementType.METHOD)//源碼中編譯時就會使用@Retention(RetentionPolicy.SOURCE)public?@interface?Override?{}此注解表面是重寫父類的方法,只能用于方法上,并且用于編譯階段,我們在開發(fā)的時候,如果注解使用不當(dāng),在源碼編譯時立馬就會做出提示。
SafeVarargs
@SafeVarargs注解是在JDK7中引入的。此注解適用于接受varargs參數(shù)的final和static方法或構(gòu)造函數(shù)。此注解用于確保方法不會對其varargs參數(shù)執(zhí)行不安全的操作。從Java9開始,@SafeVarargs注解也適用于私有實例方法。
@SafeVarargs源碼
????@Documented@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.CONSTRUCTOR,?ElementType.METHOD})public?@interface?SafeVarargs?{}使用案例
如果不使用此注解
public class SafevarargsTest {public static void main(String[] args) {// 傳遞可變參數(shù),參數(shù)是泛型集合display(10, 20, 30);// 傳遞可變參數(shù),參數(shù)是非泛型集合display("10", 20, 1000L); // 會有編譯警告}public static <T> void display(T... array) {for (T arg : array) {System.out.println(arg.getClass().getName() + ":" + arg);}} }display方法會提示
如果我們給這個方法添加此注解后
上面的代碼在可變參數(shù)display前添加了@SafeVarargs注解,當(dāng)然也可以使用 @SuppressWarnings("unchecked") 注解,可是,兩者相比較來說的話@SafeVarargs注解更適合。
注意:@SafeVarargs注解不適用于非static或非final聲明的方法,對于未聲明為static或final的方法,假如,要抑制unchecked警告,可以使用@SuppressWarnings注解。
SuppressWarnings
用于有選擇的關(guān)閉編譯器對類、方法、成員變量、變量初始化的警告。
????@Target({TYPE,?FIELD,?METHOD,?PARAMETER,?CONSTRUCTOR,?LOCAL_VARIABLE})@Retention(RetentionPolicy.SOURCE)public?@interface?SuppressWarnings?{String[]?value();}編譯時期就會做出提示,使用范圍也是比較廣泛,可以適用于類、接口、枚舉、方法、字段等。
我們開發(fā)過程中見過最多的應(yīng)該是集合存放數(shù)據(jù)的時候,比如下面的例子。
但是如果我們把第一行代碼注釋放開。
這樣就不會提示了。
FunctionalInterface
JDK8新增注解。@FunctionalInterface標(biāo)記在接口上,“函數(shù)式接口”是指僅僅只包含一個抽象方法的接口。
源碼
????@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public?@interface?FunctionalInterface?{}1、該注解只能標(biāo)記在”有且僅有一個抽象方法”的接口上。
2、JDK8接口中的靜態(tài)方法和默認(rèn)方法,都不算是抽象方法。
3、接口默認(rèn)繼承java.lang.Object,所以如果接口顯示聲明覆蓋了Object中方法,那么也不算抽象方法。
4、該注解不是必須的,如果一個接口符合”函數(shù)式接口”定義,那么加不加該注解都沒有影響。加上該注解能夠更好地讓編譯器進(jìn)行檢查。如果編寫的不是函數(shù)式接口,但是加上了@FunctionInterface,那么編譯器會報錯。
5、@FunctionalInterface 注解的interface。它的特點是其中只有一個子類必須要實現(xiàn)的abstract方法。
使用場景
Callable、Runnable等就有使用到。
????@FunctionalInterfacepublic?interface?Callable<V>?{V?call()?throws?Exception;}@FunctionalInterfacepublic?interface?Runnable?{public?abstract?void?run();}Deprecated
用于標(biāo)識方法或類,標(biāo)識該類或方法已過時,建議不要使用 。
????@Documented@Retention(RetentionPolicy.RUNTIME)@Target(value={CONSTRUCTOR,?FIELD,?LOCAL_VARIABLE,?METHOD,?PACKAGE,?PARAMETER,?TYPE})public?@interface?Deprecated?{}使用范圍就想到廣泛了,構(gòu)造方法、字段、方法等。
注意:只是提示過時了,不建議使用,不代表不能用,但是我們?nèi)绻胗媚硞€使用此注解標(biāo)記的方法或者類的時候,建議找找有沒有替換方案,實在沒有替換方案,搞清楚為什么它會被設(shè)置成過時,使用不當(dāng)可能會對我們的程序造成你意想不到問題,也可能會挖坑。
自定義注解
終于來到自定義了,下面我們來自定義一個注解。
使用@interface自定義注解時,自動繼承了java.lang.annotation.Annotation接口,由編譯程序自動完成其他細(xì)節(jié)。在定義注解時,不能繼承其他的注解或接口。@interface用來聲明一個注解,其中的每一個方法實際上是聲明了一個配置參數(shù)。方法的名稱就是參數(shù)的名稱,返回值類型就是參數(shù)的類型(返回值類型只能是基本類型、Class、String、enum)。可以通過default來聲明參數(shù)的默認(rèn)值。
格式
????public?@interface?注解名?{定義體}?案例
自定義我們的注解
????@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public?@interface??MyInterface?{String?value()?default?"";}使用階段為運行階段,目標(biāo)是在方法上。定義一個屬性value默認(rèn)值為空字符串。
下面我們就來使用我們定義的注解。
????import?java.lang.reflect.Method;public?class?InterfaceDemo?{//@MyInterface("老田自定義的注解")//@MyInterface@MyInterface(value?=?"老田自定義的注解")public?void?test()?{//?TODO:}public?static?void?main(String[]?args)?{InterfaceDemo?interfaceDemo?=?new?InterfaceDemo();Class<?>?clazz?=?interfaceDemo.getClass();Method[]?methods?=?clazz.getMethods();for?(Method?method?:?methods)?{if?("test".contentEquals(method.getName()))?{System.out.println("方法名稱=?"?+?method.getName());MyInterface?myInterface?=?method.getAnnotation(MyInterface.class);System.out.println("方法上的注解=?"?+?myInterface);System.out.println("枚舉值=?"?+?myInterface.value());}}}}如果沒有指定value的值,那就使用默認(rèn)值,value兩種方式賦值:
????@MyInterface("老田自定義的注解")@MyInterface(value?=?"老田自定義的注解")然后我們運行上面的代碼,輸出結(jié)果為:
以上我們就搞定了自定義注解以及使用,獲取屬性值。
注解是普通類還是接口?
使用javap查看我們自定義的注解.class文件內(nèi)容。
這里Annotation是接口:
????public?interface?Annotation?{}那么證明,我們自定義的注解是extend接口Annotation,由此可知注解就是接口。
自定義注解注意點
解參數(shù)的可支持?jǐn)?shù)據(jù)類型:
所有基本數(shù)據(jù)類型(int,float,boolean,byte,double,char,long,short)
String類型
Class類型
enum類型
Annotation類型
以上所有類型的數(shù)組
Annotation類型里面的參數(shù)該怎么設(shè)定:
只能用public或默認(rèn)(default)這兩個訪問權(quán)修飾.例如,String value();這里把方法設(shè)為defaul默認(rèn)類型。
參數(shù)成員只能用基本類型byte,short,char,int,long,float,double,boolean八種基本數(shù)據(jù)類型 和 String,Enum,Class,annotations等數(shù)據(jù)類型,以及這一些類型的數(shù)組.例如,String value();這里的參數(shù)成員就為String。
如果只有一個參數(shù)成員,最好把參數(shù)名稱設(shè)為"value",后加小括號.例:下面的例子FruitName注解就只有一個參數(shù)成員。
如何獲取注解?
共有以下五種方式:
獲取類上的注解:Class類的getAnnotation()
獲取方法上的注解:Method類的getAnnotation()
獲取字段上的注解:Field類的 getAnnotation()
獲取構(gòu)造方法上的注解:Constructor類的getAnnotation()
獲取包上的注解:Package類的getAnnotation()
如果此元素上存在指定的注釋類型,則此方法返回該元素的注釋,否則返回null。從上面的的集中方式中發(fā)現(xiàn),都是使用getAnnotation()方法獲取的,相信大多數(shù)人都能猜到為什么都是同一個方法名稱。
下面就來說說Java中注解獲取的鼻祖:
java.lang.reflect.AnnotatedElement此接口所有方法
看看AnnotatedElement類圖:
發(fā)現(xiàn)前面說的獲取注解的類,全部都實現(xiàn)了AnnotatedElement接口。
所以程序通過反射獲取了某個類的AnnotatedElement對象之后,程序就可以調(diào)用該對象的方法。
如下四個個方法來訪問Annotation信息:
「getAnnotation」
返回該程序元素上存在的、指定類型的注解,如果該類型注解不存在,則返回null。
「getAnnotations」
返回該程序元素上存在的所有注解。
「isAnnotationPresent」
判斷該程序元素上是否包指定類型的注解,存在則返回true,否則返回false。
「getDeclaredAnnotations」
返回直接存在于此元素上的所有注釋。與此接口中的其他方法不同,該方法將忽略繼承的注釋。(如果沒有注釋直接存在于此元素上,則返回長度為零的一個數(shù)組。)該方法的調(diào)用者可以隨意修改返回的數(shù)組;這不會對其他調(diào)用者返回的數(shù)組產(chǎn)生任何影響
總結(jié)
為什么要學(xué)習(xí)注解?元注解有哪些?基本注解有哪些?如何自定義注解?注解是普通類還是接口?自定義注解需要注意些什么?獲取獲取注解?
參考:
qqe2.com/java/post/1853.html cnblogs.com/peida/archive/2013/04/24/3036689.htmlIT技術(shù)分享社區(qū)
個人博客網(wǎng)站:https://programmerblog.xyz
文章推薦程序員效率:畫流程圖常用的工具程序員效率:整理常用的在線筆記軟件遠(yuǎn)程辦公:常用的遠(yuǎn)程協(xié)助軟件,你都知道嗎?51單片機程序下載、ISP及串口基礎(chǔ)知識硬件:斷路器、接觸器、繼電器基礎(chǔ)知識
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的后端JAVA:说说你对【注解】的理解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python没有那个文件或目录_fata
- 下一篇: 常用经验命令集