不詩意的女程序媛不是好廚師~
轉載請註明出處,From李詩雨—[https://blog.csdn.net/cjm2484836553/article/details/104432728]
《重學Java系列之---註解基礎》
按照慣例我們先來看下本篇的思維導圖:
本篇來和大家先學習一下註解基礎,後期再一步一步深入學習。
本篇的主要目的就是先對註解有些基本的瞭解,後期會進行大量的代碼實戰,這樣循序漸進,相信我們就可以很好的掌握註解了。
1.什麼是註解?
【註解的定義】:
Java 註解用於爲 Java 代碼提供元數據。作爲元數據,註解不直接影響你的代碼執行,但也有一些類型的註解實際上可以用於這一目的。Java 註解是從 Java5 開始添加到 Java 的。
【通俗點理解】:
註解即標籤。
如果把代碼想象成一個具有生命的個體,註解就是給這些代碼的某些個體打標籤。
2.自定義註解及使用
- 註解通過 @interface關鍵字進行定義。
public @interface Test {
}
它的形式跟接口很類似,不過前面多了一個 @ 符號。
上面的代碼就創建了一個名字爲 Test 的註解。你可以簡單理解爲創建了一張名字爲 Test的標籤。
- 使用註解
@Test
public class TestAnnotation {
}
創建一個類 TestAnnotation,然後在類定義的地方加上 @Test就可以用 Test註解這個類了。
你可以簡單理解爲將 Test 這張標籤貼到 TestAnnotation這個類上面。
3.元註解
3.1元註解的理解
元註解:即註解的註解。
元註解是可以註解到註解上的註解,或者說元註解是一種基本註解,但是它能夠應用到其它的註解上面。
如果難於理解的話,你可以這樣理解。元註解也是一張標籤,但是它是一張特殊的標籤,它的作用和目的就是給其他普通的標籤進行解釋說明的。
3.2 常見的5種元註解。
元註解有 @Retention、@Documented、@Target、@Inherited、@Repeatable 5 種。
下面分別一一來說明:
@Retention — 指定了註解的存活時間
Retention 的英文意爲保留期的意思。當 @Retention 應用到一個註解上的時候,它解釋說明了這個註解的的存活時間。
它的取值如下:
- RetentionPolicy.SOURCE 註解只在源碼階段保留,在編譯器進行編譯時它將被丟棄忽視。 ----生命週期最短的註解
- RetentionPolicy.CLASS 註解只被保留到編譯進行的時候,它並不會被加載到 JVM 中。
- RetentionPolicy.RUNTIME 註解可以保留到程序運行的時候,它會被加載進入到 JVM 中,所以在程序運行時可以獲取到它們
我畫一張圖來幫助大家理解 這三個取值下 註解 所能保留到的時期:
@Target ---- 指定註解用在什麼地方
Target 是目標的意思,@Target 指定了註解運用的地方
你可以這樣理解,當一個註解被 @Target 註解時,這個註解就被限定了運用的場景。
類比到標籤,原本標籤是你想張貼到哪個地方就到哪個地方,但是因爲 @Target 的存在,它張貼的地方就非常具體了,比如只能張貼到方法上、類上、方法參數上等等。@Target 有下面的取值
- ElementType.ANNOTATION_TYPE 可以給一個註解進行註解
- ElementType.CONSTRUCTOR 可以給構造方法進行註解
- ElementType.FIELD 可以給屬性進行註解
- ElementType.LOCAL_VARIABLE 可以給局部變量進行註解
- ElementType.METHOD 可以給方法進行註解
- ElementType.PACKAGE 可以給一個包進行註解
- ElementType.PARAMETER 可以給一個方法內的參數進行註解
@Inherited — 允許子類繼承父類的註解。
Inherited 是繼承的意思,但是它並不是說註解本身可以繼承,而是說如果一個超類被 @Inherited 註解過的註解進行註解的話,那麼如果它的子類沒有被任何註解應用的話,那麼這個子類就繼承了超類的註解。
這麼說比較抽象,舉個栗子給大家看看:
現在有一個 美女類Beauty,還有一個它的子類 BeautyChild。
那麼當Beauty類被打上了“美女”的標籤後,她的孩子BeautyChild默認也會擁有“美女標籤”。
(PS:也就是美女的孩子還是美女。)
//1.自定義一個使用了@Inherited的標籤
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface InheritedTest {
String value();
}
//2.美女類Beauty,並且擁有一個“美女”標籤
@InheritedTest("美女")
public class Beauty {
}
//3.美女類的孩子類BeautyChild,並沒有特地指定任何標籤
class BeautyChild extends Beauty {
}
//4.測試:看從子類中能否取到標籤
public class DemoTest {
public static void main(String[] args) {
Class<BeautyChild> beautyChildClass = BeautyChild.class;
if (beautyChildClass.isAnnotationPresent(InheritedTest.class)) {
System.out.println(beautyChildClass.getAnnotation(InheritedTest.class).value());
}
}
}
結果是:從子類中確實取到了父類所擁有的標籤。
@Repeatable — 註解的值可以同時取多個
Repeatable 是可重複的意思。@Repeatable 是 Java 1.8 才加進來的,所以算是一個新的特性。
什麼樣的註解會多次應用呢?通常是註解的值可以同時取多個。
還是來舉個栗子來方便大家理解吧:
比如說我吧,在工作上,我擁有“程序媛”的標籤;在朋友眼中,我擁有“瘋一樣的女子”的標籤;在陌生人眼中,我又擁有“淑女”的標籤。根據不同的場合,我可以擁有很多不同的標籤,這完全沒有問題。
//1.先聲明一個Persons類用來包含所有的身份
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Persons {
Person[] value();//由於會有多個身份,所以此處是數組
}
//2.定義一個Person註解,它使用到了@Repeatable。
//@Repeatable括號內的就相當於用來保存該註解內容的容器。
@Repeatable(Persons.class)
public @interface Person {
String role() default "";
}
//3.聲明一個Shiyu類,給該類加上一些身份。
@Person(role = "程序媛")
@Person(role = "瘋一樣的女子")
@Person(role = "淑女")
public class Shiyu {
}
//4.測試看看能否獲取這多個標籤
public class DemoTest2 {
public static void main(String[] args) {
if (Shiyu.class.isAnnotationPresent(Persons.class)) {
Persons p2 = Shiyu.class.getAnnotation(Persons.class);
for (Person t : p2.value()) {
System.out.println(t.role());
}
}
}
}
結果標籤全部可以打印出來,則正確:
【注意事項】:
大家在測試@repeatable的代碼時,別忘了配置gradle哈。要加上下邊這個配置纔可以:
即要加上:
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
如果不加的話,編譯時會報錯:
所以,切記這些小細節哈~
@Documented—用到的很少
顧名思義,這個元註解肯定是和文檔有關。它的作用是能夠將註解中的元素包含到 Javadoc 中去。ElementType.TYPE 可以給一個類型進行註解,比如類、接口、枚舉
4.註解的屬性
註解的屬性也叫做成員變量。
註解只有成員變量,沒有方法。
【需要注意】的是:
-
在註解中定義屬性時它的類型是有要求的。
只能用8種基本數據類型( byte,short,char,int,long,float,double,boolean),外加 類(Class),接口(interface),註解(annotation), String,以及這一些類的數組。
-
註解中屬性可以有默認值,默認值需要用 default 關鍵值指定
如:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Test{ int id() default -1; String msg() default "Hello"; }
上面代碼定義了 TestAnnotation 這個註解中擁有 id 和 msg 兩個屬性。
在使用的時候,我們應該給它們進行賦值。
賦值的方式是在註解的括號內以 value="" 形式,多個屬性之前用 ,隔開:
@Test(id=1,msg="hello annotation") public class TestAnnotation { }
5.註解的提取
1.運行期註解(RunTime)—利用反射去獲取信息。
2.編譯期(Compile time)註解,以及處理編譯期註解的手段是APT和Javapoet,@Retention(RetentionPolicy.CLASS)。
今天我們主要講一下運行期註解的提取:
首先可以通過 Class 對象的 isAnnotationPresent() 方法判斷它是否應用了某個註解
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {}
然後通過 getAnnotation() 方法來獲取 Annotation 對象。
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}
或者是 getAnnotations() 方法。
public Annotation[] getAnnotations() {}
前一種方法返回指定類型的註解,後一種方法返回註解到這個元素上的所有註解。
如果獲取到的 Annotation 如果不爲 null,則就可以調用它們的屬性方法了。比如
public class TestDemo {
public static void main(String[] args) {
boolean hasAnnotation = TestDemo.class.isAnnotationPresent(Test.class);
if (hasAnnotation) {
TestAnnotation testAnnotation = TestDemo.class.getAnnotation(Test.class);
System.out.println("id:" + testAnnotation.id());
System.out.println("msg:" + testAnnotation.msg());
}
}
}
6.註解的使用場景
- 提供信息給編譯器: 編譯器可以利用註解來探測錯誤和警告信息
- 編譯階段時的處理: 軟件工具可以用來利用註解信息來生成代碼、Html文檔或者做其它相應處理。
- 運行時的處理: 某些註解可以在程序運行的時候接受代碼的提取
值得注意的是,註解不是代碼本身的一部分。
積累點滴,做好自己~