Java注解 (Annotation)
A.使用注解
1.什么是注解
- 注解是放在Java源代码的类、方法、字段、参数前的一种标签
2.注解的作用
- 注解本身对代码逻辑没有任何影响
- 如何使用注解有工具(编译器)决定
- 编译器可以使用注解
3.定义配置参数
-
配置参数由注解类型定义
-
配置参数可以包括:
- 所有基本类型
- String
- 枚举类型
- 数组
-
配置参数必须是常量
-
缺少某个配置参数将使用默认值
-
如果只写常量,相当于省略了value参数。(如果参数名称是value,可以省略参数名称)
-
如果只写注解,相当于全部使用默认值
public class A{
@Check(min=0,max=100,value=20)
private int m;
@Check(value=20)
private int n;
@Check(20) // 相当于 @Check(value=20)
private int x;
@Check
private int y;
}
B.定义注解
1.使用@interface定义注解
- 注解的参数类似无参数方法
- 可以设定一个默认值(推荐)
- 把最常用的参数命名为value(推荐)
public @interface Report{
int type() default 0;
String level() default "info";
String value() default "";
}
2.元注解
2.1使用@Target定义注解可以被应用于源码的位置:
- 类或接口: ElementType.TYPE
- 字段: ElementType.FIELD
- 方法: ElementType.METHOD
- 构造方法: ElementType.CONSTRUCTOR
- 方法参数: ElementType.PARAMETER
//只能用于方法的@Report注解
@Target( ElementType.METHOD)
public @interface Report{
int type() default 0;
String level() default "info";
String value() default "";
}
//用于方法和字段的@Report注解
@Target({
ElementType.METHOD,
ElementType.FIELD
})
public @interface Report{
int type() default 0;
String level() default "info";
String value() default "";
}
2.2使用@Retention定义注解的生命周期
- 仅编译期:RetentionPolicy.SOURCE
- 仅class文件:RetentionPolicy.CLASS
- 运行期:RetentionPolicy:RUNTIME
注意:@Retention不存在,则该注解默认为CLASS。通常自定义的注解都是RUNTIME
- 注解的生命周期
- RetetionPolicy.SOURCE:编译器在编译时直接丢弃,如@Override
- RetetionPolicy.CLASS:该注解仅存储在class文件中
- RetentionPolicy.RUNTIME:在运行期可以读取该注解
2.3使用@Repeatable定义注解是否可重复
- 条件是JDK版本>=1.8
@Repeatable
@Target( ElementType.TYPE)
public @interface Report{
int type() default 0;
String level() default "info";
String value() default "";
}
@Report(type=1,level="debug")
@Report(type=2,level="warning")
public class hello(){
}
2.4使用@Inherited定义子类是否可继承父类定义的注解
- 仅针对@Target为TYPE类型的注解
- 仅针对class的继承
- 对interface的继承无效
@Inherited
@Target( ElementType.TYPE)
public @interface Report{
int type() default 0;
String level() default "info";
String value() default "";
}
@Report(type=1)
public class Person(){
}
public class Student extends Person(){
}
2.5小结,定义注解的步骤
-
用@interface定义注解
-
用元注解(meta annotation)配置注解
- Target : 必须设置
- Retention : 一般设置为RUNTIME
- 通常不必写@Inherited,@Repeatable等等
-
定义注解参数和默认值
@Target( ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Report{
int type() default 0;
String level() default "info";
String value() default "";
}
C.处理注解
1.如何读取RUNTIME类型的注解?方法是是用:反射
- 注解也是class
- 所有的注解继承自java.lang.annotation.Annotation
- 使用反射API
2.使用反射API读取注解,判断注解是否存在
- Class.isAnnotationPresent(Class) //用isAnnotationPresent(Class)判断一个注解是否存在
- Field.isAnnotationPresent(Class)
- Method.isAnnotationPresent(Class)
- Constructor.isAnnotationPresent(Class)
Clss cls = Person.class;
//判断@Report是否存在
cls.isAnnotationPresent(Report.class);
3.使用getAnnotation(Class)来获得一个注解
//使用getAnnotation(Class)来获得一个注解,如果不存在则返回null
Report report = cls.getAnnotation("Report.class");
int type = report.type();
String level = report.level();
4.使用isAnnotationPresent(Class)来判断一个注解是否存在
//判断一个annotation是否存在
Clss cls = Person.class;
if(cls.isAnnotationPresent(Report.class)){
Report report = cls.getAnnotation(Report.class);
if(report != null){
...
}
}
5.读取方法参数的注解
public String hello(@NotNull @Range(max=6) String name, @NotNull String prefix){
...
}
//由于读取方法参数的注解,
//方法参数名作为一个数组,而每个方法参数又可以定义多个注解,
//所以一次获得方法的所有注解,就有必须用二维数据来接收.
/*
annos 的值为:
{
{@NotNull, @Range(max=5)},
{@NotNull}
}
*/
Method m = ...
Annotation[][] annos = m.getParameterAnnotation();
Annotation[] annosOfName = annos[0]; //name参数的Annotation
for(Annotation anno : annosOfName){
if(anno instanceof Range){
Range r = (Range) anno;
...
}
}
6.使用注解的综合实例
- 定义两个注解
//NotNull.java
@Retention(RetentionPolicy.RUNTIME) //不要漏写,否则运行时不起作用
@Target(ElementType.FIELD)
public @interface NotNull{
}
//Range.java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Range{
int min() default 1;
int max() default 100;
}
- 定义一个测试类Person
//Person.java
public class Person{
@NotNull
private String name;
@Range(max = 22)
private int age;
public Person(String name ,int age){
this.name = name;
this.age = age;
}
public static void main(String args[]) throws Exception{
Person p1 = new Person("xiao ming",25);
Person p2 = new Person(null,10);
checkPerson(p1);
checkPerson(p2);
}
static void checkPerson(Person p) throws Exception{
System.out.prinln("check" + p + "...");
Class cls = Person.class;
for (Field f : cls.getFields()){
checkField(f, p);
}
}
static void checkField(Field f, Person p) throws Exception{
if(f.isAnnotationPresent(NotNull.class)){
Object o = f.get(p);
if(o == null){
System.out.prinln("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.prinln("Error:field"+f.getName+"is out of range.");
}
}
}
}
the end.