目录
一、简介
二、注解分类
2.1、由编译器使用的注解
2.2、由工具处理.class 文件的注解
2.3、程序运行期能够读取到注解,加载后一直存在 JVM 的注解
三、定义注解
3.1、元注解
3.2、注解配置参数类型
3.3、@Target
3.4、@Retention
3.5、@Repeatable
3.6、@Inherited
3.7、定义注解
3.7.1、用@interface 定义注解
3.7.2、添加参数和默认值
3.7.3、用元注解配置注解
四、处理注解
4.1、使用注解
注解是放在 Java 源码类、方法、字段、参数前的一种特殊 ”注释“ 。
注释是被编译器直接忽略,注解则可以被编译器打包进 class 文件,因此,注解是一种用作标注的 ”元数据“。
从 JVM 的角度看,注解本身对代码逻辑没有任何影响,如何使用注解完全由工具决定。
例如:
@Override:让编译器检查该方法是否正确地实现了覆写。
@SuppressWarnings:告诉编译器忽略此处代码产生的警告。
这类注解救不活被编译进入 .class 文件,它们在编译后就被编译器扔掉了。
示例
Main.java 类
public class Main{public static void main(String[] args) {A a = new B();a.say();}}interface A{void say();
}
class B implements A{@Overridepublic void say() {}
}
编译后的文件
Main.class
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//public class Main {public Main() {}public static void main(String[] args) {A a = new B();a.say();}
}
A.class
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//interface A {void say();
}
B.class
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//class B implements A {B() {}public void say() {}
}
B.class 文件里 @Override 注解编译后就被编译器扔掉了。
比如工具会在加载class的时候,对class做动态修改,实现一些特殊功能。
例如:
@Data IntelliJ IDEA 工具可以自动生成get与set方法并且重写equals和toString方法
示例
Person.java
import lombok.Data;@Data
public class Person {private String username;public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}
}
Person.class
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//public class Person {private String username;public String getUsername() {return this.username;}public void setUsername(String username) {this.username = username;}public Person() {}public boolean equals(Object o) {if (o == this) {return true;} else if (!(o instanceof Person)) {return false;} else {Person other = (Person)o;if (!other.canEqual(this)) {return false;} else {Object this$username = this.getUsername();Object other$username = other.getUsername();if (this$username == null) {if (other$username != null) {return false;}} else if (!this$username.equals(other$username)) {return false;}return true;}}}protected boolean canEqual(Object other) {return other instanceof Person;}public int hashCode() {int PRIME = true;int result = 1;Object $username = this.getUsername();int result = result * 59 + ($username == null ? 43 : $username.hashCode());return result;}public String toString() {return "Person(username=" + this.getUsername() + ")";}
}
在程序运行期能够读取的注解,它们加载后一直存在于 JVM 中, 这也是最常用的注解。
例如:
@Controller Spring 标记控制类注解
@Service Spring 标记业务层注解
@Controller
public class ApiController {
}
ApiController.java文件 和 编译后文件ApiController.class 是一样的。@Controller 也一直存在。
Java 语言使用 @Interface 语法来定义注解(Annotation)。
示例
public @interface Report {int type() default 0;String level() default "info";String value() default "";
}
注解参数类似无参数方法,可以用 default 设置一个默认值(推荐)。比较常用参数名为value 。
有一些注解可以修饰其他的注解,这些注解就称为元注解(meta annotation)。Java 标准库已经定义了一些元注解,只需使用注解,通常不需要自己写元注解。
定义一个注解时,还可以配置参数,配置参数可以包括
最常用的元注解是 @Target 。作用是定义的注解能够应用源码的哪些位置。
示例
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;@Target({ElementType.METHOD,ElementType.FIELD
})
public @interface Report {
}
定义注解 Report 可以使用在方法和字段上。
@Retention 作用是定义注解的生命周期。
如果注解@Retention不存在,默认 仅class文件:RetentionPolicy.CLASS 。通常自定义注解都是运行期RetentionPolicy.RUNTIME这个元注解。
示例
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;@Retention(RetentionPolicy.RUNTIME)
public @interface Report {int type() default 0;String level() default "info";String value() default "";
}
@Repeatable作用是这个注解是否可以重复使用。
示例
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Target;@Report(type=1, level="debug")
@Report(type=2, level="warning")
public class Main{public static void main(String[] args) {}}
@Repeatable(Reports.class)
@Target(ElementType.TYPE)
@interface Report {int type() default 0;String level() default "info";String value() default "";
}@Target(ElementType.TYPE)
@interface Reports {Report[] value();
}
@Inherited作用 子类是否可以继承父类定义的注解。注意@Inherited只对@Target(ElementType.TYPE)类型的注解有效,并且对继承的类有效,对接口的继承无效。
示例
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Target;@Inherited
@Target(ElementType.TYPE)
public @interface Report {int type() default 0;String level() default "info";String value() default "";
}
在使用的时候,如果一个类用到了@Report
@Report(type=1)
public class Person {
}
则它的子类默认也定义了该注解:
public class Student extends Person {
}
public @interface Report {
}
public @interface Report {int type() default 0;String level() default "info";String value() default "";
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Report {int type() default 0;String level() default "info";String value() default "";
}
其中必须设置@Target 和@Retention 。 @Retention 一般设置为运行期(RetentionPolicy.RUNTIME),@Inherited 和 @ Repeatable一般可以不写。
判断某个注解是否存在于类、字段、方法或构造器。
示例
// 判断@Report是否存在于Person类:
Person.class.isAnnotationPresent(Report.class);
使用反射API读取注解
示例
// 获取Person定义的@Report注解:
Report report = Person.class.getAnnotation(Report.class);
int type = report.type();
String level = report.level();
定义一个Range.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 0;int max() default 255;
}
Person.java使用注解
public class Person {@Range(min=1, max=20)public String name;@Range(max=10)public String city;
}
测试
import java.lang.reflect.Field;public class Main{public static void main(String[] args) {Person p = new Person();p.name="张无忌";p.city="深圳";try {Main.check(p);} catch (ReflectiveOperationException e) {e.printStackTrace();}}static void check(Person person) throws IllegalArgumentException, ReflectiveOperationException {// 遍历所有Field:for (Field field : person.getClass().getFields()) {// 获取Field定义的@Range:Range range = field.getAnnotation(Range.class);// 如果@Range存在:if (range != null) {// 获取Field的值:Object value = field.get(person);// 如果值是String:if (value instanceof String) {String s = (String) value;// 判断值是否满足@Range的min/max:if (s.length() < range.min() || s.length() > range.max()) {throw new IllegalArgumentException("Invalid field: " + field.getName());}}}}}}
通过@Range 注解,配合 check() 方法就可以完成Person 示例的检查。
上一篇:C# 拖放操作