# 第四章第二节--处理注解
标签(空格分隔): 廖雪峰
---
## 注解的处理分类
- 注解本身对代码逻辑没有任何影响
- 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)
![判断是不是Annotation](http://p0b921qfc.bkt.clouddn.com/18-5-18/1429737.jpg)
方法2:直接获取
- Class.getAnnotation(Class)
- Field.getAnnotation(Class)
- Method.getAnnotation(Class)
- Constructor.getAnnotation(Class)
![获取Annotation](http://p0b921qfc.bkt.clouddn.com/18-5-18/55968607.jpg)
两种使用反射API来读取Annotation的方式:
![反射读取Annotation](http://p0b921qfc.bkt.clouddn.com/18-5-18/42287307.jpg)
例子:
定义一个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函数式编程