一 注解作用或意義
-
定義
Java 注解(Annotation)又稱 Java 標注吞加,是 JDK5.0 引入的一種注釋機制裙犹。 注解是元數(shù)據(jù)的一種形式,提供有關(guān)于程序但不屬于程序本身的數(shù)據(jù)衔憨。注解對它們注解的代碼的操作沒有直接影響叶圃。 -
意義
注解本身沒有任何意義,單獨的注解就是一種注釋践图,他需要結(jié)合其他如反射掺冠、插樁等技術(shù)才有意義。
二 注解聲明
-
聲明一個注解類型
Java中所有的注解,默認實現(xiàn)Annotation 接口:
public interface Annotation {
boolean equals(Object var1);
int hashCode();
String toString();
Class<? extends Annotation> annotationType();
}
與聲明一個"Class"不同的是德崭,注解的聲明使用@interface 關(guān)鍵字斥黑。一個注解的聲明如下:
public @interface Lance {
}
三 元注解
在定義注解時,注解類也能夠使用其他的注解聲明眉厨。對注解類型進行注解的注解類锌奴,我們稱之為 metaannotation(元注解)
一般的,我們在定義自定義注解時憾股,需要指定的元注解有兩個 :
另外還有@Documented 與 @Inherited 元注解鹿蜀,前者用于被javadoc工具提取成文檔,后者表示允許子類
繼承父類中定義的注解服球。
1. @Target
注解標記另一個注解茴恰,以限制可以應(yīng)用注解的 Java 元素類型。目標注解指定以下元素類型之一作為其值:
- ElementType.ANNOTATION_TYPE 可以應(yīng)用于注解類型斩熊。
- ElementType.CONSTRUCTOR 可以應(yīng)用于構(gòu)造函數(shù)往枣。
- ElementType.FIELD 可以應(yīng)用于字段或?qū)傩浴?/li>
- ElementType.LOCAL_VARIABLE 可以應(yīng)用于局部變量。
- ElementType.METHOD 可以應(yīng)用于方法級注解座享。
- ElementType.PACKAGE 可以應(yīng)用于包聲明婉商。
- ElementType.PARAMETER 可以應(yīng)用于方法的參數(shù)。
- ElementType.TYPE 可以應(yīng)用于類的任何元素渣叛。
2. @Retenton
注解指定標記注解的存儲方式:
- RetentionPolicy.SOURCE - 標記的注解僅保留在源級別中,并被編譯器忽略盯捌。
- RetentionPolicy.CLASS - 標記的注解在編譯時由編譯器保留淳衙,但 Java 虛擬機(JVM)會忽略。
- RetentionPolicy.RUNTIME - 標記的注解由 JVM 保留饺著,因此運行時環(huán)境可以使用它箫攀。
@Retention 三個值中 SOURCE < CLASS < RUNTIME,即CLASS包含了SOURCE幼衰,RUNTIME包含SOURCE靴跛、
CLASS。下文會介紹他們不同的應(yīng)用場景渡嚣。
下面來看例子:
//@Target(ElementType.TYPE) 只能在類上標記該注解
@Target({ElementType.TYPE,ElementType.FIELD}) // 允許在類與類屬性上標記該注解
@Retention(RetentionPolicy.SOURCE) //注解保留在源碼中
public @interface Lance {
}
四 注解類型元素
在上文元注解中梢睛,允許在使用注解時傳遞參數(shù)。我們也能讓自定義注解的主體包含 annotation type element (注解
類型元素) 聲明识椰,它們看起來很像方法绝葡,可以定義可選的默認值。
public @interface Lance {
String value(); //無默認值
int age() default 1; //有默認值
}
注意:在使用注解時腹鹉,如果定義的注解中的類型元素?zé)o默認值藏畅,則必須進行傳值。
@Lance("技術(shù)好") //如果只存在value元素需要傳值的情況功咒,則可以省略:元素名=
@Lance(value="技術(shù)好",age = 2)
int i;
五 注解的保留級別以及應(yīng)用場景
SOURCE
1. IDE語法檢查
在Android開發(fā)中愉阎, support-annotations 與androidx.annotation) 中均有提供@IntDef 注解绞蹦,此注解的定義如
下:
@Retention(SOURCE) //源碼級別注解
@Target({ANNOTATION_TYPE})
public @interface IntDef {
int[] value() default {};
boolean flag() default false;
boolean open() default false;
}
- 首先看看枚舉方法如何語法檢查的
public enum Week {
MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY,SUNDAY
}
//枚舉方法
public static void testEnum(Week week){
String str = "";
switch (week) {
case MONDAY:
str = ">今天星期一";
break;
case TUESDAY:
str = ">今天星期二";
break;
case WEDNESDAY:
str = "今天星期三";
break;
case THURSDAY:
str = ">今天星期四";
break;
case FRIDAY:
str = ">今天星期五";
break;
case SATURDAY:
str = ">今天星期六";
break;
case SUNDAY:
str = ">今天周末";
break;
}
System.out.print(str);
}
Java中Enum(枚舉)的實質(zhì)是特殊單例的靜態(tài)成員變量,在運行期所有枚舉類作為單例榜旦,全部加載到內(nèi)存中幽七。比常量多5到10倍的內(nèi)存占用。
- 而現(xiàn)在為了進行內(nèi)存優(yōu)化章办,我們現(xiàn)在不再使用枚舉锉走,則方法定義為:
public static final int MONDAY = 0;
public static final int TUESDAY = 1;
public static final int WEDNESDAY = 2;
public static final int THURSDAY = 3;
public static final int FRIDAY = 4;
public static final int SATURDAY = 5;
public static final int SUNDAY = 6;
//改為
public static void testEnum(int week){
case MONDAY:
str = ">今天星期一";
break;
case TUESDAY:
str = ">今天星期二";
break;
case WEDNESDAY:
........
}
但是因為是基本類型 無法限制傳遞參數(shù) 可以傳遞0—6 之外任何值,所以這種方法是不行的
- 采用注解可以替代枚舉 且可以達到代碼檢查 限制參數(shù)傳遞的效果
public static final int MONDAY = 0;
public static final int TUESDAY = 1;
public static final int WEDNESDAY = 2;
public static final int THURSDAY = 3;
public static final int FRIDAY = 4;
public static final int SATURDAY = 5;
public static final int SUNDAY = 6;
@IntDef(value = {MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY,SUNDAY})//限定
@Target({ElementType.PARAMETER})//作用于參數(shù)的注解
@Retention(RetentionPolicy.SOURCE)//保留級別為源碼
public @interface week{
}
public static void testAn(@week int week) {
String str = "";
switch (week) {
case 0:
str = "今天星期一";
break;
case 1:
str = "今天星期二";
break;
case 2:
str = "今天星期三";
break;
case 3:
str = "今天星期四";
break;
case 4:
str = "今天星期五";
break;
case 5:
str = "今天星期六";
break;
case 6:
str = "今天周末";
break;
}
System.out.print(str);
}
這里有個注意點:Add library 'Gradle: com.android.support:support-annotations:27.1.1@jar' to classpath
在編寫Android程序時藕届,我們經(jīng)常使用@NonNull
挪蹭,@UiThread
,@IntegerRes
等這些Android提供的注解:但是當(dāng)你想在一個 Java Library中使用 support-annotations時,你卻發(fā)現(xiàn)gradle
一直彈出 :Add library 'Gradle: com.android.support:support-annotations:X.X.X@jar' to classpath 這個時候可以自己下載support-annotations 手動添加休偶、或者使用SDK已下載好的 手動添加到lib路徑下即可
2. APT注解處理器
APT全稱為:"Anotation Processor Tools"梁厉,意為注解處理器。顧名思義踏兜,其用于處理注解
CLASS
定義為CLASS 的注解词顾,會保留在class文件中,但是會被虛擬機忽略(即無法在運行期反射獲取注解)碱妆。此時完全符合
此種注解的應(yīng)用場景為字節(jié)碼操作肉盹。如:AspectJ、熱修復(fù)Roubust中應(yīng)用此場景疹尾。
所謂字節(jié)碼操作即為上忍,直接修改字節(jié)碼Class文件以達到修改代碼執(zhí)行邏輯的目的。在程序中有多處需要進行是否
登錄的判斷纳本。
RUNTIME
注解保留至運行期窍蓝,意味著我們能夠在運行期間結(jié)合反射技術(shù)獲取注解中的所有信息。