注解學習
Java中所有的注解,默認實現(xiàn) Annotation 接口:
package java.lang.annotation;
public interface Annotation {
boolean equals(Object obj);
int hashCode();
String toString();
Class<? extends Annotation> annotationType();
}
元注解
在定義注解時,注解類也能夠使用其他的注解聲明。對注解類型進行注解的注解類,我們稱之為 meta-annotation(元注解)。聲明的注解允許作用于哪些節(jié)點使用@Target聲明;保留級別由@Retention 聲明。其中保留級別如下。
另外還有@Documented 與 @Inherited 元注解,前者用于被javadoc工具提取成文檔,后者表示允許子類 繼承父類中定義的注解。
@Target
注解標記另一個注解,以限制可以應用注解的 Java 元素類型。目標注解指定以下元素類型之一作為其值:
- ElementType.ANNOTATION_TYPE 可以應用于注解類型。
- ElementType.CONSTRUCTOR 可以應用于構(gòu)造函數(shù)。
- ElementType.FIELD 可以應用于字段或?qū)傩浴?/li>
- ElementType.LOCAL_VARIABLE 可以應用于局部變量。
- ElementType.METHOD 可以應用于方法級注解。
- ElementType.PACKAGE 可以應用于包聲明。
- ElementType.PARAMETER 可以應用于方法的參數(shù)。
- ElementType.TYPE 可以應用于類的任何元素。
@Retention
RetentionPolicy.SOURCE
標記的注解僅保留在源級別中,并被編譯器忽略。
在Android中我們需要設計接口以供使用者調(diào)用時,如出現(xiàn)需要對入?yún)⑦M行類型限定,如限定為資源ID、布局ID等類型參數(shù),將參數(shù)類型直接給定int即可。然而,我們可以利用Android為我們提供的語法檢查注解,來輔助進行更為直接的參數(shù)類型檢查與提示。
參數(shù)限制為:圖片資源ID。
public Drawable getDrawable(@DrawableRes int id) throws NotFoundException
同時,我們也可以通過利用@Intdef來定義自己的入?yún)㈩愋蜋z查。
Java中Enum(枚舉)的實質(zhì)是特殊單例的靜態(tài)成員變量,在運行期所有枚舉類作為單例,全部加載到內(nèi)存中。 比常量多5到10倍的內(nèi)存占用。
此注解的意義在于能夠取代枚舉,實現(xiàn)如方法入?yún)⑾拗啤?/h5>
//每一個成員就是一個Wek對象
enum WeekDay {
SUNDAY, MONDAY
}
//每一個成員就是一個Wek對象
enum WeekDay {
SUNDAY, MONDAY
}
可以修改為如下:
private static final int SUNDAY = 0;
private static final int MONDAY = 1;
@IntDef({SUNDAY, MONDAY})
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.SOURCE)
@interface WekDay { //注解
}
@WekDay
private static int mCurrentIntDay;
public static void setCurrentDay(@WekDay int currentDay) {
mCurrentIntDay = currentDay;
}
public static void setDrawable(@DrawableRes int id) {
}
調(diào)用方法:
setDrawable(R.drawable.ic_launcher_background);
setCurrentDay(SUNDAY);
RetentionPolicy.CLASS
標記的注解在編譯時由編譯器保留,但 Java 虛擬機(JVM)會忽略。會保留在class文件中,但是會被虛擬機忽略(即無法在運行期反射獲取注解)。此時完全符合。
此種注解的應用場景為字節(jié)碼操作。所謂字節(jié)碼操作即為,直接修改字節(jié)碼Class文件以達到修改代碼執(zhí)行邏輯的目的。在程序中有多處需要進行是否 登錄的判斷。
如果我們使用普通的編程方式,需要在代碼中進行 if-else 的判斷,也許存在十個判斷點,則需要在每個判斷點加 入此項判斷。此時,我們可以借助AOP(面向切面)編程思想,將程序中所有功能點劃分為: 需要登錄 與 無需登錄 兩種類型,即兩個切面。對于切面的區(qū)分即可采用注解。
//Java源碼
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface Login {
}
@Login
public void jumpA(){
startActivity(new Intent(this,AActivity.class));
}
public void jumpB(){
startActivity(new Intent(this,BActivity.class));
}
在上訴代碼中, jumpA 方法需要具備登錄身份。而 Login 注解的定義被設置為 CLASS 。因此我們能夠在該類所編 譯的字節(jié)碼中獲得到方法注解 Login 。在操作字節(jié)碼時,就能夠根據(jù)方法是否具備該注解來修改class中該方法的 內(nèi)容加入 if-else 的代碼段:
//Class字節(jié)碼
@Login
public void jumpA() {
if (this.isLogin) {
this.startActivity(new Intent(this, LoginActivity.class));
} else {
this.startActivity(new Intent(this, AActivity.class));
}
}
public void jumpB() {
startActivity(new Intent(this,BActivity.class));
}
注解能夠設置類型元素(參數(shù)),結(jié)合參數(shù)能實現(xiàn)更為豐富的場景,如:運行期權(quán)限判定等。
RetentionPolicy.RUNTIME
標記的注解由 JVM 保留,因此運行時環(huán)境可以使用它。
SOURCE < CLASS < RUNTIME,即CLASS包含了SOURCE,RUNTIME包含SOURCE、CLASS
根據(jù)注解的保留級別不同,對注解的使用自然存在不同場景。
| 級別 | 技術(shù) | 說明 |
|---|---|---|
| 源碼 | 作用于源碼級別的注解,可提供給IDE語法檢查、APT等場景使用。 | 在編譯期能夠獲取注解與注解聲明的類包括類中所有成員信息,一般用于生成額外的輔助類。在類中使用 SOURCE 級別的注解,其編譯之后的class中會被丟棄。 |
| 字節(jié)碼 | 字節(jié)碼增強 ,AspectJ、熱修復Roubust中應用此場景 | 在編譯出Class后,通過修改Class數(shù)據(jù)以實現(xiàn)修改代碼邏輯目的。對于是否需要修改的區(qū)分或者修改為不同邏輯的判斷可以使用注解。 |
| **運行時 ** | 反射 | 在程序運行期間,通過反射技術(shù)動態(tài)獲取注解與其元素,從而完成不同的邏輯判定。 |