您好,登錄后才能下訂單哦!
這篇文章主要講解了“如何理解注解”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“如何理解注解”吧!
本文主要內(nèi)容如下:
現(xiàn)在已經(jīng)處于注解盛行時(shí)代,注解@Override ,這個(gè)注解是再熟悉不過了,還有@Controller、@RequestMapping、@Service.....
注解已經(jīng)是作為一個(gè)開發(fā)中必備的技能了。
如果在面試中被問到注解,說不出個(gè)123,就只能回去等通知了。
注解annotation是JavaSE5.0中新增功能。可以理解為注解是一種標(biāo)記,這種標(biāo)記可以在編譯、類加載、運(yùn)行時(shí)被讀取,并執(zhí)行相應(yīng)的處理。
它可以添加到程序的任何元素上:包聲明、類型聲明、構(gòu)造方法、普通方法、成員變量、參數(shù)。
注解的老大:
package java.lang.annotation; //是個(gè)接口 public interface Annotation { boolean equals(Object obj); int hashCode(); String toString(); //獲取注解類型 Class<? extends Annotation> annotationType(); }
JDK自帶為我們提供了元注解,下面就來聊聊JDK的元注解。
JDK為我們提供五個(gè)元注解,位于java.lang.annotation 包目錄下。分別為:
@Retention
@Target
@Documented
@Inherited
@Repeatable
字面翻譯:
該注解就是定義該注解是作用于什么階段。
編譯、類加載、運(yùn)行(使用最多)
注解源碼:
@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源代碼中,編譯器編譯的時(shí)候會(huì)把它直接丟棄。
CLASS:編譯器將注解記錄在.class文件中。當(dāng)運(yùn)行Java程序時(shí),JVM不能獲取注解信息。這是默認(rèn)值。
RUNTIME:編譯器將注解記錄在.class文件中。當(dāng)運(yùn)行Java程序時(shí),JVM也能獲取注解信息。程序可以通過反射獲取注解信息。
注意:如果使用該注解的時(shí)候必須指定value的值。
字面意義為目標(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)建一個(gè)注解的時(shí)候,指示該注解可以作用于程序中的哪些元素。
它里邊也包含一個(gè)名為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用于描述其它類型的annotation應(yīng)該被作為被標(biāo)注的程序成員的公共API,因此可以被例如javadoc此類的工具文 檔化。Documented是一個(gè)標(biāo)記注解,沒有成員 。
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Documented { }
主要是用來生成文檔的,工作中基本上很少使用,作為了解就可以了。
字面意義:
@Inherited注解是在聲明創(chuàng)建一個(gè)注解的時(shí)候,指定該注解將具有繼承性。
Inherited 源碼:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Inherited { }
如果某個(gè)類使用了該注解@Xx,則其子類也將自動(dòng)被此注解@Xx所修飾。
使用模板:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Inherited public @interface A { } @A public class Base { } //Sub也帶有@A public class Sub extends Base { }
@Repeatable元注解,顧名思義,重復(fù)注解,就是在聲明創(chuàng)建注解的時(shí)候,指定該注解可以被同一個(gè)程序元素多次使用。這是JDK8新增的注解,重復(fù)注解只是一種簡(jiǎn)單化寫法,這種簡(jiǎn)單化寫法是一種假象。多個(gè)重復(fù)注解其實(shí)會(huì)被作為“容器”注解的value成員變量的數(shù)組元素。
比如下面Spring中注解ComponentScan:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Repeatable(ComponentScans.class) public @interface ComponentScan { } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented public @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 { @Bean public User person() { return new User(); } }
注解必須使用工具來處理,工具負(fù)責(zé)提取注解中包含的元數(shù)據(jù),工具還會(huì)根據(jù)這些元數(shù)據(jù)增加額外的功能。下面我們先來了解一下5個(gè)基本注解的用法。
Override
SafeVarargs
SuppressWarnings
FunctionalInterface
Deprecated
下面我們就來說說這五個(gè)注解。
用于標(biāo)識(shí)方法,標(biāo)識(shí)該方法屬于重寫父類的方法 。
//只能用于方法上 @Target(ElementType.METHOD) //源碼中編譯時(shí)就會(huì)使用 @Retention(RetentionPolicy.SOURCE) public @interface Override { }
此注解表面是重寫父類的方法,只能用于方法上,并且用于編譯階段,我們?cè)陂_發(fā)的時(shí)候,如果注解使用不當(dāng),在源碼編譯時(shí)立馬就會(huì)做出提示。
@SafeVarargs注解是在JDK7中引入的。此注解適用于接受varargs參數(shù)的final和static方法或構(gòu)造函數(shù)。此注解用于確保方法不會(huì)對(duì)其varargs參數(shù)執(zhí)行不安全的操作。從Java9開始,@SafeVarargs注解也適用于私有實(shí)例方法。
@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); // 會(huì)有編譯警告 } public static <T> void display(T... array) { for (T arg : array) { System.out.println(arg.getClass().getName() + ":" + arg); } } }
display方法會(huì)提示
如果我們給這個(gè)方法添加此注解后
上面的代碼在可變參數(shù)display前添加了@SafeVarargs注解,當(dāng)然也可以使用 @SuppressWarnings("unchecked") 注解,可是,兩者相比較來說的話@SafeVarargs注解更適合。
注意:@SafeVarargs注解不適用于非static或非final聲明的方法,對(duì)于未聲明為static或final的方法,假如,要抑制unchecked警告,可以使用@SuppressWarnings注解。
用于有選擇的關(guān)閉編譯器對(duì)類、方法、成員變量、變量初始化的警告。
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.SOURCE) public @interface SuppressWarnings { String[] value(); }
編譯時(shí)期就會(huì)做出提示,使用范圍也是比較廣泛,可以適用于類、接口、枚舉、方法、字段等。
我們開發(fā)過程中見過最多的應(yīng)該是集合存放數(shù)據(jù)的時(shí)候,比如下面的例子。
但是如果我們把第一行代碼注釋放開。
這樣就不會(huì)提示了。
JDK8新增注解。@FunctionalInterface標(biāo)記在接口上,“函數(shù)式接口”是指僅僅只包含一個(gè)抽象方法的接口。
源碼
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface FunctionalInterface {}
1、該注解只能標(biāo)記在”有且僅有一個(gè)抽象方法”的接口上。
2、JDK8接口中的靜態(tài)方法和默認(rèn)方法,都不算是抽象方法。
3、接口默認(rèn)繼承java.lang.Object,所以如果接口顯示聲明覆蓋了Object中方法,那么也不算抽象方法。
4、該注解不是必須的,如果一個(gè)接口符合”函數(shù)式接口”定義,那么加不加該注解都沒有影響。加上該注解能夠更好地讓編譯器進(jìn)行檢查。如果編寫的不是函數(shù)式接口,但是加上了@FunctionInterface,那么編譯器會(huì)報(bào)錯(cuò)。
5、@FunctionalInterface 注解的interface。它的特點(diǎn)是其中只有一個(gè)子類必須要實(shí)現(xiàn)的abstract方法。
使用場(chǎng)景
Callable、Runnable等就有使用到。
@FunctionalInterface public interface Callable<V> { V call() throws Exception; } @FunctionalInterface public interface Runnable { public abstract void run(); }
用于標(biāo)識(shí)方法或類,標(biāo)識(shí)該類或方法已過時(shí),建議不要使用 。
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE}) public @interface Deprecated { }
使用范圍就想到廣泛了,構(gòu)造方法、字段、方法等。
注意:只是提示過時(shí)了,不建議使用,不代表不能用,但是我們?nèi)绻胗媚硞€(gè)使用此注解標(biāo)記的方法或者類的時(shí)候,建議找找有沒有替換方案,實(shí)在沒有替換方案,搞清楚為什么它會(huì)被設(shè)置成過時(shí),使用不當(dāng)可能會(huì)對(duì)我們的程序造成你意想不到問題,也可能會(huì)挖坑。
終于來到自定義了,下面我們來自定義一個(gè)注解。
使用@interface自定義注解時(shí),自動(dòng)繼承了java.lang.annotation.Annotation接口,由編譯程序自動(dòng)完成其他細(xì)節(jié)。在定義注解時(shí),不能繼承其他的注解或接口。@interface用來聲明一個(gè)注解,其中的每一個(gè)方法實(shí)際上是聲明了一個(gè)配置參數(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 ""; }
使用階段為運(yùn)行階段,目標(biāo)是在方法上。定義一個(gè)屬性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("方法名稱= ">
如果沒有指定value的值,那就使用默認(rèn)值,value兩種方式賦值:
@MyInterface("老田自定義的注解") @MyInterface(value = "老田自定義的注解")
然后我們運(yùn)行上面的代碼,輸出結(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)這兩個(gè)訪問權(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。
如果只有一個(gè)參數(shù)成員,最好把參數(shù)名稱設(shè)為"value",后加小括號(hào).例:下面的例子FruitName注解就只有一個(gè)參數(shù)成員。
共有以下五種方式:
獲取類上的注解:Class類的getAnnotation()
獲取方法上的注解:Method類的getAnnotation()
獲取字段上的注解:Field類的 getAnnotation()
獲取構(gòu)造方法上的注解:Constructor類的getAnnotation()
獲取包上的注解:Package類的getAnnotation()
如果此元素上存在指定的注釋類型,則此方法返回該元素的注釋,否則返回null。從上面的的集中方式中發(fā)現(xiàn),都是使用getAnnotation()方法獲取的,相信大多數(shù)人都能猜到為什么都是同一個(gè)方法名稱。
下面就來說說Java中注解獲取的鼻祖:
java.lang.reflect.AnnotatedElement
此接口所有方法
看看AnnotatedElement類圖:
發(fā)現(xiàn)前面說的獲取注解的類,全部都實(shí)現(xiàn)了AnnotatedElement接口。
所以程序通過反射獲取了某個(gè)類的AnnotatedElement對(duì)象之后,程序就可以調(diào)用該對(duì)象的方法。
如下四個(gè)個(gè)方法來訪問Annotation信息:
「getAnnotation」
返回該程序元素上存在的、指定類型的注解,如果該類型注解不存在,則返回null。
「getAnnotations」
返回該程序元素上存在的所有注解。
「isAnnotationPresent」
判斷該程序元素上是否包指定類型的注解,存在則返回true,否則返回false。
「getDeclaredAnnotations」
返回直接存在于此元素上的所有注釋。與此接口中的其他方法不同,該方法將忽略繼承的注釋。(如果沒有注釋直接存在于此元素上,則返回長(zhǎng)度為零的一個(gè)數(shù)組。)該方法的調(diào)用者可以隨意修改返回的數(shù)組;這不會(huì)對(duì)其他調(diào)用者返回的數(shù)組產(chǎn)生任何影響
感謝各位的閱讀,以上就是“如何理解注解”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)如何理解注解這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。