?# 第四章第二節--處理注解
標簽(空格分隔): 廖雪峰
---
## 注解的處理分類
- 注解本身對代碼邏輯沒有任何影響
- SOURCE類型的注解在編譯期就被丟棄了.
- CLASS類型的注解僅保留在class文件中,處理的時候需要專用工具進行操作.
- RUNTIME類型的注解在運行期間可以被讀取.
- 如何使用注解由工具決定.
因為個人寫的代碼不涉及編譯器,所以無法處理SOURCE類型的代碼,CLASS類型的注解同時是由處理class類型的工具來使用非常少見.只有RUNTIME類型的注解可以在運行的時候使用Java代碼來讀取,所以我們討論的如何處理注解,只針對的RUNTIME類型的注解.
## 如何讀取RUNTIME類型的注解.
- Annotation也是Class
- 所有的Annotation繼承java.lang.annotation.Annotation
- 使用反射API
方法1:判斷是否存在
使用反射API讀取Annotation:
- Class.isAnnotationPresent(Class)
- Field.isAnnotationPresent(Class)
- Method.isAnnotationPresent(Class)
- Constructor.isAnnotationPresent(Class)

方法2:直接獲取
- Class.getAnnotation(Class)
- Field.getAnnotation(Class)
- Method.getAnnotation(Class)
- Constructor.getAnnotation(Class)

兩種使用反射API來讀取Annotation的方式:

例子:
定義一個NotNull的注解
```java
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME) //必須有
public @interface NotNull {
}
```
定義一個邊界檢測的注解
```java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Range {
int min() default 1;
int max() default 100;
}
```
應用該注解的bean對象.
```java
public class Person {
@NotNull
public String name;
@Range(max = 20)
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String toString() {
return "Person(name=" + name + ", age=" + age + ")";
}
}
```
檢測類,對注解進行檢測.
```java
import java.lang.reflect.Field;
public class Main {
public static void main(String[] args) throws Exception {
Person p1 = new Person("Xiao Ming", 25);
Person p2 = new Person(null, 15);
checkPerson(p1);
checkPerson(p2);
}
static void checkPerson(Person p) throws Exception {
System.out.println("check " + p + "...");
Class c = Person.class;
for (Field f : c.getFields()) {
checkField(f, p);
}
}
static void checkField(Field f, Person p) throws Exception {
if (f.isAnnotationPresent(NotNull.class)) {
Object r = f.get(p);
if (r == null) {
System.out.println("Error: field " + f.getName() + " is null.");
}
}
if (f.isAnnotationPresent(Range.class)) {
Range range = f.getAnnotation(Range.class);
int n = (Integer) f.get(p);
if (n < range.min() || n > range.max()) {
System.out.println("Error: field " + f.getName() + " is out of range.");
}
}
}
}
```
## 小節小結
- 可以在運行期通過反射讀取RUNTIME類型的注解.(不能漏寫@Rentention(RetentionPolicy.RUNTIME))
- 可以通過工具處理注解來實現相應的功能.
- 對javaBean的屬性值規則進行檢查.
- JUnit會自動運行@Test注解的測試方法.
- 前言
- 一:Java快速入門
- 二:Java面向對象編程
- 三:Java異常處理
- 錯誤處理
- Java的異常
- 捕獲異常
- 拋出異常
- 自定義異常
- 斷言和日志
- 使用斷言
- 使用JDK Logging
- 使用Commons Logging
- 使用Log4j
- 四:Java反射與泛型
- 反射
- Class類
- 訪問字段
- 調用方法
- 調用構造方法
- 獲取繼承關系
- 注解
- 使用注解
- 定義注解
- 處理注解
- 泛型
- 什么是泛型
- 使用泛型
- 編寫泛型
- 擦拭法
- extends通配符
- super通配符
- 泛型和反射
- 五:Java集合
- Java集合簡介
- Java集合簡介
- List
- 使用List
- 編寫equals方法
- Map
- Set
- Queue
- Stack
- 最佳實踐
- 六:Java IO編程
- 七:Java處理日期和時間
- 八:JUnit單元測試
- 九:Java正則表達式
- 十:Java加密與安全
- 十一:Java多線程編程
- 十二:Maven基礎
- 十三:Java網絡編程
- 十四:Java操作XML和JSON
- 十五:Java JDBC編程
- 十六:Java函數式編程