什么是注解
注解(Annotation)是Java5的一個新特性,是插入在代碼中的一種注釋或者說是一種元數據(meta data),這些注釋信息可以在編譯期使用預編譯工具進行獲取處理,也可以在運行期使用Java反射機制來獲取,這取決于你的注解類型。
標準注解
在Java的JDK中內置了一些系統自帶的注解,這些注解也常稱為標準注解,常見的有:@Override, @Deprecated, @SuppressWarnings
@Override
@Override作用于方法,表示被標注的方法重載了父類的方法。
其實重載父類的方法也可以不寫@Override注解,但是寫上了,若該重載的方法寫錯了方法名那么在編譯期就會有出現警告
當我們寫Activity的時候,通過都會寫onCreate這個方法
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Deprecated
當一個類型或者類型成員使用@Deprecated標記則表示建議不再使用該元素。
public class User {
public int id;
public int getId() {
return id;
}
@Deprecated
public void setId(int id) {
this.id = id;
}
}
@Deprecated使用
@SuppressWarnings
@SuppressWarnings就是抑制警告,它被用于關閉編譯器對類、方法、成員變量、變量初始化的警告。
當我們定義了一個變量,但是沒有使用它的時候,會有這么一個警告。
但是我們加上@SuppressWarnings注解之后,警告就消失了
可以看到,這個注解和之前兩個有點不一樣,該注解多了一個參數。通過它的名字我們可以知道,unused
表示未使用的。@SuppressWarnings還有許多其他參數
- serial:可序列化的類上缺少serialVersionUID定義的警告
- finally:finally語句不能正常完成的警告
- deprecation:使用了過時的類型或者類型成員方法時的警告
- unchecked:執行了未檢查的轉換的警告
- all:所有情況的警告。
自定義注解
除了使用系統提供的注解,我們當然也可以自定義注解。
使用@interface自定義注解時,自動繼承了java.lang.annotation.Annotation接口,由編譯程序自動完成其他細節。在定義注解時,不能繼承其他的注解或接口。@interface用來聲明一個注解,其中的每一個方法實際上是聲明了一個配置參數。方法的名稱就是參數的名稱,返回值類型就是參數的類型(返回值類型只能是基本類型、Class、String、enum)。可以通過default來聲明參數的默認值。
定義注解格式:
public @interface 注解名 {定義體}
注解參數的可支持數據類型:
- 所有基本數據類型(int,float,boolean,byte,double,char,long,short)
- String類型
- Class類型
- enum類型
- Annotation類型
- 以上所有類型的數組
**Annotation類型里面的參數該怎么設定: **
- 只能用public或默認(default)這兩個訪問權修飾.例如,String value();這里把方法設為defaul默認類型;
- 只能用可支持的數據類型
- 如果只有一個參數成員,最好把參數名稱設為"value"。這樣在使用注解的時候,就不用在指定key了
// 這是擁有兩個屬性的自定義注解
public @interface MyAnnotation {
public String value() default "";
public int id();
}
// 這是只有一個屬性的自定義注解,所以這里推薦把屬性名設置為value,使用的時候更加方便
public @interface ValueAnnotation {
public String value() default "";
}
自定義注解的使用
@MyAnnotation(id=12)
@ValueAnnotation()
public class Test {}
獲取自定義注解屬性
自定義注解中往往都帶有屬性,那么怎么獲取這些注解的屬性值呢?
通常都是有一個套路的
private void getAnnotationValue() {
MyAnnotation annotation = null;
Class clazz = Test.class;
// 判斷是否有MyAnnotation注解存在
boolean isPresent = clazz.isAnnotationPresent(MyAnnotation.class);
if (isPresent) {
// 獲取注解
annotation = (MyAnnotation) clazz.getAnnotation(MyAnnotation.class);
// 得到注解的屬性值
String value = annotation.value();
int id = annotation.id();
System.out.println("----> annotation=" + annotation);
System.out.println("----> value=" + value + ",id=" + id);
}
}
元注解
元注解的作用就是負責注解其他注解。Java5.0定義了4個標準的meta-annotation類型,它們被用來提供對其它 annotation類型作說明。Java5.0定義的元注解:@Target、@Retention、@Documented、@Inherited
@Target:
@Target說明了Annotation所修飾的對象范圍:Annotation可被用于 packages、types(類、接口、枚舉、Annotation類型)、類型成員(方法、構造方法、成員變量、枚舉值)、方法參數和本地變量(如循環變量、catch參數)。在Annotation類型的聲明中使用了target可更加明晰其修飾的目標。
@Target有如下一些值:
public enum ElementType {
/**
* Class, interface or enum declaration.
* 用于描述類、接口(包括注解類型) 或enum聲明
*/
TYPE,
/**
* Field declaration.
* 用于描述域
*/
FIELD,
/**
* Method declaration.
* 用于描述方法
*/
METHOD,
/**
* Parameter declaration.
* 參數聲明
*/
PARAMETER,
/**
* Constructor declaration.
* 用于描述構造器
*/
CONSTRUCTOR,
/**
* Local variable declaration.
* 用于描述局部變量
*/
LOCAL_VARIABLE,
/**
* Annotation type declaration.
* 用于描述注解
*/
ANNOTATION_TYPE,
/**
* Package declaration.
* 用于描述包
*/
PACKAGE
}
使用實例: 我們來看看@Override 的源碼
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {}
可以看出來,@Override就是作用于方法的。
@Retention:
@Retention定義了Annotation的有效范圍,類似于Android中常提到的生命周期。Java文件從生產到執行,要經過三個主要的階段:java源文件,Class文件,JVM運行。
與此類似,有的Annotation僅出現在源代碼中而被編譯器丟棄,而另一些卻被編譯在Class文件中;有的編譯在Class文件中的Annotation在運行時會被虛擬機忽略,而另一些在運行時被讀取讀取。
所以,在@Retention中使用RetentionPoicy標明注解會存留到哪個階段,RetentionPoicy有三個值:
- SOURCE:在源文件中有效(即僅在源文件保留)
- CLASS:在Class文件中有效(即Class保留)
- RUNTIME:在運行時有效(即保留至運行時)
使用實例: 我們來看看@Deprecated的源碼
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Deprecated {}
可以看出來,在這段源碼中用@Retention(RetentionPolicy.RUNTIME)標明該注解會保留至運行時。
@Documented:
@Documented表示在生成javadoc文檔時將該Annotation也寫入到幫助文檔。 Documented是一個標記注解,沒有成員。
@Inherited:
@Inherited 元注解是一個標記注解,@Inherited闡述了某個被標注的類型是被繼承的。如果一個使用了@Inherited修飾的annotation類型被用于一個class,則這個annotation將被用于該class的子類。
簡單的運行時注解示例
先定義三個Runtime注解,包括類、方法、字段,
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface run_classInfo {
String value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface run_methodInfo {
String name() default "long";
String data();
int id() default 365;
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface run_fieldInfo {
int[] value();
}
然后使用它們
@run_classInfo("類注解RunTime Class")
public class RunTimeTest {
@run_fieldInfo(value = {77, 88})
public String fieldInfo = "filedInfo";
@run_fieldInfo(value = {163})
public int id = 55;
@run_methodInfo(name = "whyalwaysmea", data = "haha")
public static String getMethod() {
return RunTimeTest.class.getSimpleName();
}
}
解析注解
/**
* 解析運行時注解
*/
private void showRunTimeInfo() {
StringBuffer sb = new StringBuffer();
//獲取Class 注解
Class<?> clazz = RunTimeTest.class;
Constructor<?>[] constructors = clazz.getConstructors();
//獲取包含的注解類信息
run_classInfo runClassInfo = clazz.getAnnotation(run_classInfo.class);
if (runClassInfo != null) {
//獲取class注解
sb.append("Class注解: ").append("\n");
sb.append(Modifier.toString(clazz.getModifiers())).append(" ")
.append(clazz.getSimpleName()).append("\n");
sb.append("注解值:").append("\n")
.append(runClassInfo.value()).append("\n\n");
}
//獲取Field注解
sb.append("Field注解:").append("\n");
Field[] fields = clazz.getDeclaredFields(); //獲取自身的不包括繼承類
for (Field field : fields) {
//獲取field注解類信息
run_fieldInfo fieldInfo = field.getAnnotation(run_fieldInfo.class);
if (fieldInfo != null) {
sb.append(Modifier.toString(field.getModifiers())).append(" ")
.append(field.getType().getSimpleName()).append(" ")
.append(field.getName()).append("\n");
sb.append("注解值: ").append("\n")
.append(Arrays.toString(fieldInfo.value())).append("\n\n");
}
}
//獲取Method 注解
sb.append("Method注解: ").append("\n");
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
run_methodInfo methodInfo = method.getAnnotation(run_methodInfo.class);
if (methodInfo != null) {
sb.append(Modifier.toString(method.getModifiers())).append(" ")
.append(method.getReturnType().getSimpleName()).append(" ")
.append(method.getName()).append("\n");
sb.append("注解值:").append("\n");
sb.append("name: ").append(methodInfo.name()).append("\n");
sb.append("data: ").append(methodInfo.data()).append("\n");
sb.append("id: ").append(methodInfo.id()).append("\n");
}
}
tvDes.setText(sb.toString());
}