註解定義
Java 註解是附加在代碼中的一些元信息,用於一些工具在編譯、運行時進行解析和使用,起到說明、配置的功能。註解不會也不能影響代碼的實際邏輯,僅僅起到輔助性的作用。註解包含在 java.lang.annotation 包中。
注意:註解本身不是代碼的一部分,註解作用在類或者方法、屬性上不會自己起作用,需要其他工具(代碼)主動去獲取註解的信息。
作用
- 替代配置文件:例如spring中的中@Autowired,@Service
- 編譯時進行格式檢查:例如Override
jdk註解
- @Override,表示當前的方法定義將覆蓋超類中的方法。
- @Deprecated,使用了註解爲它的元素編譯器將發出警告,因爲註解
- @Deprecated是不贊成使用的代碼,被棄用的代碼。
- @SuppressWarnings,關閉不當編輯器警告信息。
- @Functionallnterface:java8 函數式接口註解,一般有這個註解的方法可以直接使用 lambda 表達式
元註解(定義註解的註解)
元註解是可以註解到註解上的註解,或者說元註解是一種基本註解,但是它能夠應用到其它的註解上面。基本的元標籤有:
- @Retention:定義了該註解的生命週期
- @Documented:簡單的標記註解,表示是否將註解信息添加在 Java 文檔
- @Target:表示該註解用於什麼地方
- @Inherited:定義了一個註釋與子類的關係。如果一個超類帶有 @Inherited 註解,那麼對於該超類,它的子類如果沒有被任何註解應用的話,那麼這個子類就繼承了超類的註解。
@Repeatable:@Repeatable 是 Java 8 中加入的,是指可重複的意思。通常使用 @Repeatable 的時候指註解的值可以同時取多個。
如何定義一個註解
註解的屬性也叫做成員變量。註解只有成員變量,沒有方法。註解的成員變量在註解的定義中以無形參的方法形式來聲明,其方法名定義了該成員變量的名字,其返回值定義了該成員變量的類型。
注意:註解可以有默認值,需要用 default 關鍵字指定。如果註解內只有一個名爲 value 的屬性時,應用該屬性時可以將值直接寫到括號內,不用寫 value = “…”
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Person {
int id() default 100;
String name();
String language();
String company();
}
上面假設定義了一個名爲 @Person 的註解,該註解有 id, name, language, company 三個屬性。使用的時候,我們應該對其賦值。賦值的方式類似於 key=“value” 的方式進行,屬性之間用 “,” 隔開:
@Coder(id = 10086, name = "GRQ", language = "JAVA", company = "cetc")
public class personTest() {
}
自定義註解步驟
自定義註解類編寫的規則:
- 註解類型定義爲 @interface,所有的註解會自動繼承 java.lang.Annotation
- 這一接口,而且不能再去繼承其他的類或接口;
- 參數成員只能用 public 或 default 兩個關鍵字修飾;
- 參數成員只能用基本類型:byte, short, char, int, long, float, double, boolean,以及String, Enum, Class, Annotations 等數據類型,以及這些類型的數組;
- 要獲取類方法和字段的註解信息,必須通過Java 的反射技術;
- 註解也可以不定義成員變量,這種註解具有標記作用;
- 自定義註解需要使用元註解進行編寫;
大家可以參考這篇文章
註解底層原理
從java源碼到class字節碼是由編譯器完成的,編譯器會對java源碼進行解析並生成class文件,而註解也是在編譯時由編譯器進行處理,編譯器會對註解符號處理並附加到class結構中,根據jvm規範,class文件結構是嚴格有序的格式,唯一可以附加信息到class結構中的方式就是保存到class結構的attributes屬性中。 我們知道對於類、字段、方法,在class結構中都有自己特定的表結構,而且各自都有自己的屬性,而對於註解,作用的範圍也可以不同,可以作用在類上,也可以作用在字段或方法上,這時編譯器會對應將註解信息存放到類、字段、方法自己的屬性上。
在我們的AnnotationTest.java類被編譯後,在對應的AnnotationTest.class文件中會包含一個RuntimeVisibleAnnotations屬性,由於這個註解是作用在類上,所以此屬性被添加到類的屬性集上。即Test註解的鍵值對value=test會被記錄起來。而當JVM加載AnnotationTest.class文件字節碼時,就會將RuntimeVisibleAnnotations屬性值保存到AnnotationTest的Class對象中,於是就可以通過AnnotationTest.class.getAnnotation(Test.class)獲取到Test註解對象,進而再通過Test註解對象獲取到Test裏面的屬性值。
這裏可能會有疑問,Test註解對象是什麼?其實註解被編譯後的本質就是一個繼承Annotation接口的接口,所以@Test其實就是“public interface Test extends Annotation”,當我們通過AnnotationTest.class.getAnnotation(Test.class)調用時,JDK會通過動態代理生成一個實現了Test接口的對象,並把將RuntimeVisibleAnnotations屬性值設置進此對象中,此對象即爲Test註解對象,通過它的value()方法就可以獲取到註解值。