Java中的注解詳解(Annotation)
所有的注解類型都繼承自 java.lang.annotation.Annotation 接口。
注解(Annotation)是一種引用數(shù)據(jù)類型。編譯之后也是生成 xxx.class 文件。
一個(gè)注解準(zhǔn)確意義上來(lái)說(shuō),只不過(guò)是一種特殊的注釋而已,如果沒有解析它的代碼,它可能連注釋都不如。
解析一個(gè)類或者方法的注解往往有兩種形式
一種是編譯期直接的掃描。=====> @Retention(RetentionPolicy.SOURCE)
- 編譯器的掃描指的是編譯器在對(duì) java 代碼編譯字節(jié)碼的過(guò)程中會(huì)檢測(cè)到某個(gè)類或者方法被一些注解修飾,這時(shí)它就會(huì)對(duì)于這些注解進(jìn)行某些處理。典型的就是 @Override 注解。
- 這一種情況只適用于那些編譯器已經(jīng)熟知的注解類,比如 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:允許被修飾的注解作用在類、接口和枚舉上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)前注解編譯期可見,不會(huì)寫入 class 文件RetentionPolicy.CLASS:類加載階段丟棄,會(huì)寫入 class 文件RetentionPolicy.RUNTIME:永久保存,可以反射獲取
@Documented:注解是否應(yīng)當(dāng)被包含在 JavaDoc 文檔中
@Inherited:是否允許子類繼承該注解
二、Java 中內(nèi)置的三大注解
@Override
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}它沒有任何的屬性,所以并不能存儲(chǔ)任何其他信息。它只能作用于方法之上,編譯結(jié)束后將被丟棄。
僅被編譯器可知,編譯器在對(duì) java 文件進(jìn)行編譯成字節(jié)碼的過(guò)程中,一旦檢測(cè)到某個(gè)方法上被修飾了該注解,就會(huì)去匹對(duì)父類中是否具有一個(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;
}可以修飾所有的類型,作用是,標(biāo)記當(dāng)前的類或者方法或者字段等已經(jīng)不再被推薦使用了,可能下一次的 JDK 版本就會(huì)刪除。
@SuppressWarnings
三、自定義注解
語(yǔ)法格式:
[修飾符列表] @interface 注解類型名{
類型 屬性() [defauult 值];
}
// 樣例
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String name() default "gdb";
String[] phoneNumbers() default {};
}注解當(dāng)中的屬性類型可以是:byte short int long float double boolean char String Class 枚舉類型以及以上每一種的數(shù)組形式。
四、反射注解
要求必須使用 @Retention(RetentionPolicy.RUNTIME),這樣該注解才能夠被反射到。
例子:反射類上的 @MyAnnotation 注解。
- 1.編寫自己的注解:
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.在類上使用自己的注解:
package annotation1;
@MyAnnotation(name = "zhangsan", phoneNumbers = {"123456", "654321"})
public class MyAnnotationTest {
}- 3.通過(guò)反射機(jī)制來(lái)獲取指定類上的注解信息:
package annotation1;
import java.util.Arrays;
public class ReflectAnnotation {
public static void main(String[] args) throws Exception {
//獲取類
Class<?> c = Class.forName("annotation1.MyAnnotationTest");
//判斷類上面是否有 @MyAnnotation
if (c.isAnnotationPresent(MyAnnotation.class)){
//獲取注解對(duì)象
MyAnnotation myAnnotation = (MyAnnotation)c.getAnnotation(MyAnnotation.class);
//獲取注解對(duì)象的屬性和調(diào)用接口沒有區(qū)別
System.out.println(myAnnotation.name() + ", " + Arrays.toString(myAnnotation.phoneNumbers()));
}
//判斷 Date 類上面是否有 @MyAnnotation 這個(gè)注解。
Class<?> s = Class.forName("java.lang.String");
System.out.println(s.isAnnotationPresent(MyAnnotation.class));
}
}
五、注解的作用
注解在程序當(dāng)中等同于一種標(biāo)記,如果這個(gè)元素上有這個(gè)注解怎么辦,沒有這個(gè)注解怎么辦。
應(yīng)用場(chǎng)景:假設(shè)有這樣一個(gè)注解,叫做 @Id,這個(gè)注解只能出現(xiàn)在類上面,當(dāng)這個(gè)類上有這個(gè)注解的時(shí)候,要求這個(gè)類中必須有一個(gè) int 類型的 id 屬性。如果沒有這個(gè)屬性就報(bào)異常。如果有這個(gè)屬性就正常執(zhí)行。
- 1.編寫 @Id 注解:
package annotation1;
public class NotHasIdException extends RuntimeException{
public NotHasIdException() {
}
public NotHasIdException(String message) {
super(message);
}
}- 2.編寫異常類:
package annotation1;
public class NotHasIdException extends RuntimeException{
public NotHasIdException() {
}
public NotHasIdException(String message) {
super(message);
}
}
- 3.編寫使用 @Id 注解的類:
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 {
//獲取類
Class<?> c = Class.forName("annotation1.IdAnnotationTest");
//判斷類上面是否有 @Id 注解
if (c.isAnnotationPresent(Id.class)){
//當(dāng)一個(gè)類上面有 @Id 注解的時(shí)候,要求類中必須存在 int 類型的id屬性。
//如果沒有int類型的id屬性則報(bào)異常。
//獲取類的所有屬性
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)注的類沒有int類型的id屬性!??!");
}
}
}
六、總結(jié)
- 如果一個(gè)注解的屬性的名字是 value,并且只有一個(gè)屬性的話,在使用的時(shí)候,該屬性名可以省略。
- 如果屬性是一個(gè)數(shù)組,如果數(shù)組中只有一個(gè)元素,大括號(hào)可以省略。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java滾動(dòng)數(shù)組計(jì)算編輯距離操作示例
這篇文章主要介紹了Java滾動(dòng)數(shù)組計(jì)算編輯距離操作,涉及java字符串與數(shù)組的遍歷、計(jì)算、轉(zhuǎn)換等相關(guān)操作技巧,需要的朋友可以參考下2019-12-12
jmeter實(shí)現(xiàn)接口關(guān)聯(lián)的兩種方式(正則表達(dá)式提取器和json提取器)
Jmeter用于接口測(cè)試時(shí),后一個(gè)接口經(jīng)常需要用到前一次接口返回的結(jié)果,本文主要介紹了jmeter實(shí)現(xiàn)接口關(guān)聯(lián)的兩種方式,感興趣的小伙伴們可以參考一下2021-11-11
編碼實(shí)現(xiàn)從無(wú)序鏈表中移除重復(fù)項(xiàng)(C和JAVA實(shí)例)
如果不能使用臨時(shí)緩存,你怎么實(shí)現(xiàn)無(wú)序鏈表中移除重復(fù)項(xiàng)(?C和JAVA實(shí)例無(wú)序鏈表中移除重復(fù)項(xiàng)。2013-10-10
springboot設(shè)置多環(huán)境配置文件
本文介紹了在Spring Boot項(xiàng)目中配置多個(gè)環(huán)境(如dev、test、prod)的方法,可以通過(guò)創(chuàng)建多個(gè)`.yml`文件來(lái)實(shí)現(xiàn),每個(gè)文件對(duì)應(yīng)一個(gè)環(huán)境,并在`application.yml`中指定激活的配置文件,感興趣的朋友跟隨小編一起看看吧2025-12-12
java 使用DecimalFormat進(jìn)行數(shù)字的格式化實(shí)例詳解
這篇文章主要介紹了java 使用DecimalFormat進(jìn)行數(shù)字的格式化實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-06-06
基于Java實(shí)現(xiàn)一個(gè)自己的HTTP瀏覽器
這篇文章主要為大家詳細(xì)介紹了如何基于Java實(shí)現(xiàn)一個(gè)自己的HTTP瀏覽器,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-01-01
詳解Java中字典樹(Trie樹)的圖解與實(shí)現(xiàn)
Trie又稱為前綴樹或字典樹,是一種有序樹,它是一種專門用來(lái)處理串匹配的數(shù)據(jù)結(jié)構(gòu)。本文將利用圖解詳細(xì)講解Trie樹的實(shí)現(xiàn),需要的可以參考一下2022-05-05
JNDI在JavaEE中的角色_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了JNDI在JavaEE中的角色,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-08-08

