12.注解
12.1.概念
- 注释:用文字描述程序的。给程序员看的
- 注解(Annotation):也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
- 概念描述:
- JDK1.5之后的新特性
- 说明程序的
- 使用注解:@注解名称
- 注解的保留级别:注解的保留级别限定的是注解的存在状态
- SOURCE:注解将被编译器丢弃(class文件中没有)
- CLASS:注解在class文件中可用,但会被JVM丢弃(内存中没有)
- RUNTIME:JVM在运行时,也会保留注释信息
12.2.JDK中预定义的一些注解
- @Override:检测被该注解标注的方法是否是继承自父类(接口)的
- @Deprecated:该注解标注的内容,表示已过时
- @SuppressWarnings:压制警告,一般传递参数all @SuppressWarnings(“all”)
12.3.自定义注解
/*
@interface 注解名 {
// 第一条信息:
// 名称: 方法名1
// 取值类型: 返回值类型1
返回值类型1 方法名1();
// 第二条信息
// 名称: 方法名2
// 取值类型: 返回值类型2
返回值类定2 方法名2();
...
}
*/
//自定义注解举例
@Retention(RetentionPolicy.RUNTIME)
public @interface AgeConstraint {
// 年龄的上界
int maxAge();
// 年龄的下界
int minAge();
}
@Retention(RetentionPolicy.RUNTIME)
public @interface NameLengthConstraint {
// 描述名字字符串的长度上界
int maxLength();
}
- 本质:注解本质上就是一个接口,该接口默认继承Annotation接口
- public interface MyAnno extends java.lang.annotation.Annotation {}
- 属性:接口中的抽象方法
- 注解体的格式类似于接口中的方法定义,但含义完全不同
- 方法名就是数据的名称,方法的返回值类型,表示数据值的类型。
- 每一条注解信息,它的取值的类型只能是一下几种:
- 所有的基本数据类型
- String类型
- Class类型
- 注解类型
- 以及以上类型的数组
- 定义了属性,在使用时需要给属性赋值
- 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。
- 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可。
- 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略
- 对于合法的引用类型的数据, 比如String类型, 它的默认取值不能是null
12.4.注解处理器
- 必须提供注解处理器,自定义注解才能生效
- 方式之一:反射,下面是反射类里的常用关于注解的方法
- public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass):判断变量/方法到底有没有被注解,参数为被判断的变量/方法
- public <T extends Annotation> T getAnnotation(Class<T> annotationClass):如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null。
这两个方法只能由Class、Method、Field三种反射得到的对象调用。
这两个方法位于java.lang.reflect.AnnotatedElement接口里,只不过其他类实现了它们。
- 使用步骤
- 实现注解实例
- 给注解实例中的每一个属性(每一条信息),取具体值(赋值) 取值。
- 在定义好注解类型,利用注解实例,给代码添加额外信
@注解的类型名(属性名1=属性值1,属性名2=属性值2,....)
。 - 配置注解处理器
/*
定义一个Student类,包含name和age两个成员
1.name中包含的字符个数不得超过指定值(具体的约束条件信息-> 注解)
2.age必须在指定范围内(具体的约束条件信息-> 注解)
3.name和age都满足条件才能创建Student对象,否则抛出异常。(该效果由注解处理器来实现)
通过反射技术,实现一个自定义处理器,用来Student对象
1.name和age取值满足约束,才创建对象
2.否则,抛出异常,拒绝创建
*/
//注解的实例
public class Student {
@AgeConstraint(maxAge = 25, minAge = 18)
private int age;
@NameLengthConstraint(maxLength = 5)
private String name;
private Student(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
//利用反射实现注解的功能,即注解处理器
public class StudentFactory {
// Student类对应的Class对象
private Class studentClz;
public StudentFactory() {
studentClz = Student.class;
}
public Student getStudent(int age, String name) throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 1.判断age初始化参数的取值,是否满足约束
judgeAge(age);
// 2.判断name初始化参数的取值,是否满足约束
// a. 不满足约束,直接抛出异常
// b. 否则,准备创建对象
judgeName(name);
// 3.创建Student对象并返回
Constructor constructor = studentClz.getDeclaredConstructor(int.class, String.class);
constructor.setAccessible(true);
Student st = (Student) constructor.newInstance(age, name);
return st;
}
private void judgeName(String name) throws NoSuchFieldException {
Field nameField = studentClz.getDeclaredField("name");
if (nameField.isAnnotationPresent(NameLengthConstraint.class)) {
// 如果有,获取
//System.out.println("name成员变量上,有NameLengthConstraint注解");
NameLengthConstraint nameAnnotation =
nameField.getAnnotation(NameLengthConstraint.class);
// 访问注解实例,获取约束
int length = nameAnnotation.maxLength();
// 处理
if (name.length() > length) {
// 名字长度太长
throw new IllegalArgumentException("name 参数非法:" + name);
}
}
}
private void judgeAge(int age) throws NoSuchFieldException {
// 因为对于age成员变量的约束信息,是定义在age成员变量上的,
// 该信息属于age成员的定义信息。
Field ageField = studentClz.getDeclaredField("age");
// 从该成员变量上,获取其约束信息(即注解实例)
// a. 先判断,该成员变量是否定义了,指定类型的注解实例
// Field对象.isAnnotationPresent(Class annotationType)
// 有: 返回true
// 没有: 返回false
if(ageField.isAnnotationPresent(AgeConstraint.class)) {
//System.out.println("age成员变量上,有AgeConstraint注解");
// 如果定义了AgeConstraint类型的直接实例,获取
// 获取指定类型的注解实例
// Field对象上.getAnnotation(class annotationType);
AgeConstraint ageAnnotation = ageField.getAnnotation(AgeConstraint.class);
// 访问注解实例,获取注解实例的属性值
// 注解实例.属性名()
int maxAge = ageAnnotation.maxAge();
int minAge = ageAnnotation.minAge();
// 处理
if (age < minAge || age > maxAge) {
// 不满足约束,抛出异常
throw new IllegalArgumentException("age 参数非法: " + age);
}
}
}
}
12.5.元注解
- 用于描述注解的注解
- @Target:描述注解能够作用的位置
- ElementType取值:
- ElementType.TYPE:可以作用于类上
- ElementType.METHOD:可以作用于方法上
- ElementType.FIELD:可以作用于成员变量上
- ElementType.CONSTRUCTOR:可以作用于构造方法上
- ElementType.LOCAL_VARIABLE:可以作用于局部变量上
- ElementType.PARAMETER:可以作用于方法参数前
@Target (value ={elementType.TYPE,elementType.METHOD,element.FIELD})
- ElementType取值:
- @Retention:描述注解被保留的阶段
- @Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到class字节码文件中,并被JVM读取到
- RetentionPolicy.RUNTIME
- RetentionPolicy.CLASS
- RetentionPolicy.SOURCE
@Target (value ={elementType.TYPE,elementType.METHOD,element.FIELD})
- @Documented:描述注解是否被抽取到api文档中
- @Inherited:描述注解是否被子类继承
- @Target:描述注解能够作用的位置
- 因此,我们需要在自定义注解前边加上
@Retention(RetentionPolicy.RUNTIME)
//定义一个测试类
public class Test {
public static void main(String[] args) throws
InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, NoSuchFieldException {
// 测试注解处理器
// 注解处理器
StudentFactory studentProcessor = new StudentFactory();
// 参数满足约束
//Student st1 = studentProcessor.getStudent(19, "张三");
//System.out.println(st1);
// 参数不满足约束
Student st2 = studentProcessor.getStudent(30, "1234567");
System.out.println(st2);
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/181069.html