Java中的注解詳解(Annotation)
所有的注解類(lèi)型都繼承自 java.lang.annotation.Annotation 接口。
注解(Annotation)是一種引用數(shù)據(jù)類(lèi)型。編譯之后也是生成 xxx.class 文件。
一個(gè)注解準(zhǔn)確意義上來(lái)說(shuō),只不過(guò)是一種特殊的注釋而已,如果沒(méi)有解析它的代碼,它可能連注釋都不如。
解析一個(gè)類(lèi)或者方法的注解往往有兩種形式
一種是編譯期直接的掃描。=====> @Retention(RetentionPolicy.SOURCE)
- 編譯器的掃描指的是編譯器在對(duì) java 代碼編譯字節(jié)碼的過(guò)程中會(huì)檢測(cè)到某個(gè)類(lèi)或者方法被一些注解修飾,這時(shí)它就會(huì)對(duì)于這些注解進(jìn)行某些處理。典型的就是 @Override 注解。
- 這一種情況只適用于那些編譯器已經(jīng)熟知的注解類(lèi),比如 JDK 內(nèi)置的幾個(gè)注解,而你自定義的注解,編譯器是不知道你這個(gè)注解的作用的,當(dāng)然也不知道該如何處理,往往只是會(huì)根據(jù)該注解的作用范圍來(lái)選擇是否編譯進(jìn)字節(jié)碼文件,僅此而已。
一種是運(yùn)行期反射。====> @Retention(RetentionPolicy.RUNTIME)
一、元注解
- 元注解是用于修飾注解的注解,通常用在注解的定義上。
- 元注解一般用于指定某個(gè)注解生命周期以及作用目標(biāo)等信息。
Java 中的元注解
@Target
:注解的作用目標(biāo)
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { /** * Returns an array of the kinds of elements an annotation interface * can be applied to. * @return an array of the kinds of elements an annotation interface * can be applied to */ ElementType[] value(); }
ElementType.TYPE
:允許被修飾的注解作用在類(lèi)、接口和枚舉上ElementType.FIELD
:允許作用在屬性字段上ElementType.METHOD
:允許作用在方法上ElementType.PARAMETER
:允許作用在方法參數(shù)上ElementType.CONSTRUCTOR
:允許作用在構(gòu)造器上ElementType.LOCAL_VARIABLE
:允許作用在本地局部變量上ElementType.ANNOTATION_TYPE
:允許作用在注解上ElementType.PACKAGE
:允許作用在包上
@Retention
:注解的生命周期
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { /** * Returns the retention policy. * @return the retention policy */ RetentionPolicy value(); }
RetentionPolicy.SOURCE
:當(dāng)前注解編譯期可見(jiàn),不會(huì)寫(xiě)入 class 文件RetentionPolicy.CLASS
:類(lèi)加載階段丟棄,會(huì)寫(xiě)入 class 文件RetentionPolicy.RUNTIME
:永久保存,可以反射獲取
@Documented
:注解是否應(yīng)當(dāng)被包含在 JavaDoc 文檔中
@Inherited
:是否允許子類(lèi)繼承該注解
二、Java 中內(nèi)置的三大注解
@Override
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
它沒(méi)有任何的屬性,所以并不能存儲(chǔ)任何其他信息。它只能作用于方法之上,編譯結(jié)束后將被丟棄。
僅被編譯器可知,編譯器在對(duì) java 文件進(jìn)行編譯成字節(jié)碼的過(guò)程中,一旦檢測(cè)到某個(gè)方法上被修飾了該注解,就會(huì)去匹對(duì)父類(lèi)中是否具有一個(gè)同樣方法簽名的函數(shù),如果不是,自然不能通過(guò)編譯。
@Deprecated
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE}) public @interface Deprecated { String since() default ""; boolean forRemoval() default false; }
可以修飾所有的類(lèi)型,作用是,標(biāo)記當(dāng)前的類(lèi)或者方法或者字段等已經(jīng)不再被推薦使用了,可能下一次的 JDK 版本就會(huì)刪除。
@SuppressWarnings
三、自定義注解
語(yǔ)法格式:
[修飾符列表] @interface 注解類(lèi)型名{ 類(lèi)型 屬性() [defauult 值]; } // 樣例 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String name() default "gdb"; String[] phoneNumbers() default {}; }
注解當(dāng)中的屬性類(lèi)型可以是:byte short int long float double boolean char String Class 枚舉類(lèi)型以及以上每一種的數(shù)組形式。
四、反射注解
要求必須使用 @Retention(RetentionPolicy.RUNTIME),這樣該注解才能夠被反射到。
例子:反射類(lèi)上的 @MyAnnotation 注解。
- 1.編寫(xiě)自己的注解:
package annotation1; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String name() default "gdb"; String[] phoneNumbers() default {}; }
- 2.在類(lèi)上使用自己的注解:
package annotation1; @MyAnnotation(name = "zhangsan", phoneNumbers = {"123456", "654321"}) public class MyAnnotationTest { }
- 3.通過(guò)反射機(jī)制來(lái)獲取指定類(lèi)上的注解信息:
package annotation1; import java.util.Arrays; public class ReflectAnnotation { public static void main(String[] args) throws Exception { //獲取類(lèi) Class<?> c = Class.forName("annotation1.MyAnnotationTest"); //判斷類(lèi)上面是否有 @MyAnnotation if (c.isAnnotationPresent(MyAnnotation.class)){ //獲取注解對(duì)象 MyAnnotation myAnnotation = (MyAnnotation)c.getAnnotation(MyAnnotation.class); //獲取注解對(duì)象的屬性和調(diào)用接口沒(méi)有區(qū)別 System.out.println(myAnnotation.name() + ", " + Arrays.toString(myAnnotation.phoneNumbers())); } //判斷 Date 類(lèi)上面是否有 @MyAnnotation 這個(gè)注解。 Class<?> s = Class.forName("java.lang.String"); System.out.println(s.isAnnotationPresent(MyAnnotation.class)); } }
五、注解的作用
注解在程序當(dāng)中等同于一種標(biāo)記,如果這個(gè)元素上有這個(gè)注解怎么辦,沒(méi)有這個(gè)注解怎么辦。
應(yīng)用場(chǎng)景:假設(shè)有這樣一個(gè)注解,叫做 @Id,這個(gè)注解只能出現(xiàn)在類(lèi)上面,當(dāng)這個(gè)類(lèi)上有這個(gè)注解的時(shí)候,要求這個(gè)類(lèi)中必須有一個(gè) int 類(lèi)型的 id 屬性。如果沒(méi)有這個(gè)屬性就報(bào)異常。如果有這個(gè)屬性就正常執(zhí)行。
- 1.編寫(xiě) @Id 注解:
package annotation1; public class NotHasIdException extends RuntimeException{ public NotHasIdException() { } public NotHasIdException(String message) { super(message); } }
- 2.編寫(xiě)異常類(lèi):
package annotation1; public class NotHasIdException extends RuntimeException{ public NotHasIdException() { } public NotHasIdException(String message) { super(message); } }
- 3.編寫(xiě)使用 @Id 注解的類(lèi):
package annotation1; @Id public class IdAnnotationTest { int id; }
- 4.通過(guò)反射機(jī)制來(lái)判斷是否正確:
package annotation1; import java.lang.reflect.Field; public class ReflectAnnotation { public static void main(String[] args) throws Exception { //獲取類(lèi) Class<?> c = Class.forName("annotation1.IdAnnotationTest"); //判斷類(lèi)上面是否有 @Id 注解 if (c.isAnnotationPresent(Id.class)){ //當(dāng)一個(gè)類(lèi)上面有 @Id 注解的時(shí)候,要求類(lèi)中必須存在 int 類(lèi)型的id屬性。 //如果沒(méi)有int類(lèi)型的id屬性則報(bào)異常。 //獲取類(lèi)的所有屬性 Field[] fields = c.getDeclaredFields(); boolean isOK = false; for(Field field : fields){ if("id".equals(field.getName()) && "int".equals(field.getType().getSimpleName())){ isOK = true; break; } } //判斷是否合法 if (!isOK) throw new NotHasIdException("被@Id注解標(biāo)注的類(lèi)沒(méi)有int類(lèi)型的id屬性?。。?); } } }
六、總結(jié)
- 如果一個(gè)注解的屬性的名字是 value,并且只有一個(gè)屬性的話,在使用的時(shí)候,該屬性名可以省略。
- 如果屬性是一個(gè)數(shù)組,如果數(shù)組中只有一個(gè)元素,大括號(hào)可以省略。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
詳解Java的MyBatis框架中的緩存與緩存的使用改進(jìn)
很多人在使用MyBatis的緩存后經(jīng)常會(huì)遇到MySQL分頁(yè)查詢(xún)的顯示問(wèn)題,針對(duì)于此,這里我們就來(lái)詳解Java的MyBatis框架中的緩存與緩存的使用改進(jìn),首先來(lái)回顧一下MyBatis的緩存機(jī)制與執(zhí)行:2016-06-06Springboot詳解RocketMQ實(shí)現(xiàn)消息發(fā)送與接收流程
這篇文章主要介紹了SpringBoot整合RocketMQ實(shí)現(xiàn)消息發(fā)送和接收功能,我們使用主流的SpringBoot框架整合RocketMQ來(lái)講解,使用方便快捷,本文分步驟給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-06-06詳解Java如何在業(yè)務(wù)代碼中優(yōu)雅的使用策略模式
這篇文章主要為大家介紹了Java如何在業(yè)務(wù)代碼中優(yōu)雅的使用策略模式,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的可以了解下2023-08-08在lambda的foreach遍歷中break退出操作(lambda foreach break)
這篇文章主要介紹了在lambda的foreach遍歷中break退出操作(lambda foreach break),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09springboot開(kāi)發(fā)擴(kuò)展springmvc實(shí)現(xiàn)解析
這篇文章主要介紹了springboot開(kāi)發(fā)擴(kuò)展springmvc實(shí)現(xiàn)解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02Java中Map實(shí)現(xiàn)線程安全的3種方式
本文主要介紹了Java中Map實(shí)現(xiàn)線程安全的3種方式,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03java編程實(shí)現(xiàn)基于UDP協(xié)議傳輸數(shù)據(jù)的方法
這篇文章主要介紹了java編程實(shí)現(xiàn)基于UDP協(xié)議傳輸數(shù)據(jù)的方法,較為詳細(xì)的分析了UDP協(xié)議的原理及Java編程實(shí)現(xiàn)數(shù)據(jù)傳輸客戶(hù)端與服務(wù)器端的相關(guān)技巧,需要的朋友可以參考下2015-11-11MyBatis-Plus 批量插入數(shù)據(jù)的操作方法
spring boot+mybatis plus環(huán)境,單條插入用的是BaseMapper自帶的insert方法,本文重點(diǎn)給大家介紹MyBatis-Plus 批量插入數(shù)據(jù)的操作方法,感興趣的朋友一起看看吧2021-09-09