## 注解的定義
注解(Annotation),也叫元數據。一種代碼級別的說明。它是JDK1.5及以后版本引入的一個特性,與類、接口、枚舉是在同一個層次。它可以聲明在包、類、字段、方法、局部變量、方法參數等的前面,用來對這些元素進行說明,注釋。
Annotation(注解)可以用于創建文檔,跟蹤代碼中的依賴性,甚至執行基本編譯時檢查。注解是以‘@注解名’在代碼中存在的,根據注解參數的個數,我們可以將注解分為:標記注解、單值注解、完整注解三類。它們都不會直接影響到程序的語義,只是作為注解(標識)存在,我們可以通過反射機制編程實現對這些元數據(用來描述數據的數據)的訪問。
另外,你可以在編譯時選擇代碼里的注解是否只存在于源代碼級,或者它也能在class文件、或者運行時中出現(SOURCE/CLASS/RUNTIME)。
## 作用分類
如果要對于元數據的作用進行分類,還沒有明確的定義,不過我們可以根據它所起的作用,大致可分為三類:
- 編寫文檔:通過代碼里標識的元數據生成文檔【生成文檔doc文檔】
- 代碼分析:通過代碼里標識的元數據對代碼進行分析【使用反射】
- 編譯檢查:通過代碼里標識的元數據讓編譯器能夠實現基本的編譯檢查【Override】
## 基本內置注解
#### @Override
它的作用是對覆蓋超類中方法的方法進行標記,如果被標記的方法并沒有實際覆蓋超類中的方法,則編譯器會發出錯誤警告。
```java
public class OverrideDemoTest {
private String name;
@Override
public String toString() {
return "OverrideDemoTest [name=" + name + "]";
}
}
```
#### @Deprecated
它的作用是對不應該再使用的方法添加注解,當編程人員使用這些方法時,將會在編譯時顯示提示信息,它與javadoc里的@deprecated標記有相同的功能,準確的說,它還不如javadoc @deprecated,因為它不支持參數,使用@Deprecated的示例代碼示例如下:
```java
public class DeprecatedDemoTest {
public static void main(String[] args) {
// 使用DeprecatedClass里聲明被過時的方法
DeprecatedClass.DeprecatedMethod();
}
}
class DeprecatedClass {
@Deprecated
public static void DeprecatedMethod() {
}
}
```
#### @SuppressWarnings
其參數有:
| 參數名稱 | 參數作用 |
| ------------ | ------------ |
| deprecation | 使用了過時的類或方法時的警告 |
| unchecked | 執行了未檢查的轉換時的警告 |
| fallthrough | 當 switch 程序塊直接通往下一種情況而沒有 break 時的警告 |
| path,在類路徑 | 源文件路徑等中有不存在的路徑時的警告 |
| serial | 當在可序列化的類上缺少serialVersionUID 定義時的警告 |
| finally | 任何 finally 子句不能正常完成時的警告 |
| all | 關于以上所有情況的警告 |
```java
import java.util.ArrayList;
import java.util.List;
public class SuppressWarningsDemoTest {
public static List list = new ArrayList();
@SuppressWarnings("unchecked")
public void add(String data) {
list.add(data);
}
}
```
## 自定義注解
```java
// 自定義注解
@interface NewAnnotation {
// 為自定義注解添加變量
String value();
}
// 定義一個枚舉類型,然后將參數設置為該枚舉類型,并賦予默認值
@interface Greeting {
public enum FontColor {
BLUE, RED, GREEN
};
String name();
FontColor fontColor() default FontColor.RED;
}
public class AnnotationTest {
// 使用自定義的注解類型
@NewAnnotation("mainmethod")
public static void main(String[] args) {
saying();
sayHelloWithDefaultFontColor();
sayHelloWithRedFontColor();
}
@NewAnnotation(value = "saymethod")
public static void saying() {
}
// 此時的fontColor為默認的RED
@Greeting(name="defaultfontcolor")
public static void sayHelloWithDefaultFontColor() {
}
// 將fontColor改為BLUE
@Greeting(name="notdefault", fontColor=Greeting.FontColor.BLUE)
public static void sayHelloWithRedFontColor() {
}
}
```
## 注解高級應用
#### 使用范圍
用@Target指定ElementType屬性
```java
public enum ElementType {
// 用于類,接口,枚舉但不能是注解
TYPE,
// 字段上,包括枚舉值
FIELD,
// 方法,不包括構造方法
METHOD,
// 方法的參數
PARAMETER,
// 構造方法
CONSTRUCTOR,
// 本地變量或catch語句
LOCAL_VARIABLE,
// 注解類型(無數據)
ANNOTATION_TYPE,
// Java包
PACKAGE
}
```
具體例子:
```java
import java.lang.annotation.Target;
@Target({ ElementType.METHOD, ElementType.CONSTRUCTOR })
public @interface Greeting {
// 使用枚舉類型
public enum FontColor {
BLUE, RED, GREEN
};
String name();
FontColor fontColor() default FontColor.RED;
}
```
#### 注解保持性策略
在Java編譯器編譯時,它會識別在源代碼里添加的注解是否還會保留,這就是RetentionPolicy。下面是Java定義的RetentionPolicy枚舉:
編譯器的處理有三種策略:
- 將注解保留在編譯后的類文件中,并在第一次加載類時讀取它;
- 將注解保留在編譯后的類文件中,但是在運行時忽略它;
- 按照規定使用注解,但是并不將它保留到編譯后的類文件中。
```java
public enum RetentionPolicy {
// 此類型會被編譯器丟棄
SOURCE,
// 此類型注解會保留在class文件中,但JVM會忽略它
CLASS,
// 此類型注解會保留在class文件中,JVM會讀取它
RUNTIME
}
```
```java
import java.lang.annotation.Retention;
// 讓保持性策略為運行時態,即將注解編碼到class文件中,讓虛擬機讀取
@Retention(RetentionPolicy.RUNTIME)
public @interface Greeting {
// 使用枚舉類型
public enum FontColor {
BLUE, RED, GREEN
};
String name();
FontColor fontColor() default FontColor.RED;
}
```
#### 文檔化功能
Java提供的Documented元注解跟Javadoc的作用是差不多的,其實它存在的好處是開發人員可以定制Javadoc不支持的文檔屬性,并在開發中應用。它的使用跟前兩個也是一樣的,簡單代碼示例如下
```java
import java.lang.annotation.Documented;
// 讓它定制文檔化功能
// 使用此注解時必須設置RetentionPolicy為RUNTIME
@Documented
public @interface Greeting {
// 使用枚舉類型
public enum FontColor {
BLUE,RED,GREEN
};
String name();
FontColor fontColor() default FontColor.RED;
}
```
#### 標注繼承
```java
import java.lang.annotation.Inherited;
// 讓它允許繼承,可作用到子類
@Inherited
public @interface Greeting {
// 使用枚舉類型
public enum FontColor {
BLUE, RED, GREEN
};
String name();
FontColor fontColor() default FontColor.RED;
}
```
## 讀取方法
屬于重點,在系統中用到注解權限時非常有用,可以精確控制權限的粒度。
注意:要想使用反射去讀取注解,必須將Retention的值選為Runtime。
```java
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
//讀取注解信息
public class ReadAnnotationInfoTest {
public static void main(String[] args) throws Exception {
// 測試AnnotationTest類,得到此類的類對象
Class c = Class.forName("java.lang.annotation.Annotation");
// 獲取該類所有聲明的方法
Method[] methods = c.getDeclaredMethods();
// 聲明注解集合
Annotation[] annotations;
// 遍歷所有的方法得到各方法上面的注解信息
for (Method method : methods) {
// 獲取每個方法上面所聲明的所有注解信息
annotations = method.getDeclaredAnnotations();
// 再遍歷所有的注解,打印其基本信息
System.out.println(method.getName());
for (Annotation an : annotations) {
System.out.println("方法名為:" + method.getName() + "其上面的注解為:" + an.annotationType().getSimpleName());
Method[] meths = an.annotationType().getDeclaredMethods();
// 遍歷每個注解的所有變量
for (Method meth : meths) {
System.out.println("注解的變量名為:" + meth.getName());
}
}
}
}
}
```
運行結果:
```
equals
toString
hashCode
annotationType
```