1. 四个元注解
- @Target:用于描述注解的使用范围
通过枚举类ElementType的10个常量来指定使用范围:
TYPE(类,接口), FIELD(属性),METHOD(方法),PARAMETER(形参),CONSTRUCTOR(构造器),
LOCAL_VARIABLE(局部变量),ANNOTATION_TYPE(注释类型),PACKAGE(包),TYPE_PARAMETER(类型参数),TYPE_USE(使用类型)
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* 返回可应用注释类型的元素类型的数组。
*/
ElementType[] value();
}
- @Retention:用于描述注解的生命周期
通过枚举类RetentionPolicy的3个常量来指定
SOURCE(源代码):注释将被编译器丢弃
CLASS(字节码):注释将由编译器记录在类文件中,但不需要在运行时由VM保留。这是默认行为。
RUNTIME(运行时):注释将由编译器记录在类文件中,并在运行时由VM保留,因此它们可能被反射地读取。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* 返回retention 代理
*/
RetentionPolicy value();
}
- @Documented:指示带有类型的注释默认情况下由javadoc和类似工具记录。
- @Inherited:表示自动继承注释类型
2. 自定义注解格式
[元注解]
[修饰符] @interface 注解名{
[成员列表]
}
返回值类型只能是八种基本数据类型、String类型、Class类型、enum类型、Annotation类型、及其数组类型
如果自定义注解含有抽象方法,除非它有默认值,否则使用时必须指定返回值。格式为“方法名 = 返回值”,如果只有一个抽象方法(建议使用方法名value)需要赋值,且方法名为value,可以省略“value=”。
3. 自定义注解案例
自定义注解MyAnnotation111
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
//用来给学生的性别赋值
public @interface MyAnnotation {
String value();
}
自定义注解MyAnnotation222
@Documented
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
//用来给学生的stuId和stuName赋值
public @interface MyAnnotation222 {
String stuId();
String stuName();
}
自定义类Student
@MyAnnotation222(stuName = "张三", stuId = "20240101023505")
public class Student {
private String stuName;
private String stuId;
@MyAnnotation111("人妖")
private String gender;
//构造器
public Student(String stuName, String stuId, String gender) {
this.stuName = stuName;
this.stuId = stuId;
this.gender = gender;
}
public String getStuName() {
return stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
public String getStuId() {
return stuId;
}
public void setStuId(String stuId) {
this.stuId = stuId;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public String toString() {
return "Student{" +
"stuName='" + stuName + '\'' +
", stuId='" + stuId + '\'' +
", gender=" + gender +
'}';
}
//最核心的代码,用来获取Student对象的性别注释,并将性别值修改为注释的value值
public static Student modifyGender(Student student) throws IllegalAccessException {
// 获取User类中所有的属性(getFields无法获得private属性)
Field[] fields = Student.class.getDeclaredFields();
// 遍历所有属性
for (Field field : fields) {
// 如果属性上有此注解,则进行赋值操作
if (field.isAnnotationPresent(MyAnnotation111.class)) {
MyAnnotation111 annotation111 = field.getAnnotation(MyAnnotation111.class);
//设置可以访问private属性
field.setAccessible(true);
// 设置属性的性别值为注释的值
String value = annotation111.value();
field.set(student, value);
}
}
return student;
}
}
4. 测试类StudentTest
public class StudentTest {
//测试并获取类上的注释
@Test
public void test1(){
MyAnnotation222 annotation222 = Student.class.getAnnotation(MyAnnotation222.class);
System.out.println("stu_name = " + annotation222.stuName() + " id: " + annotation222.stuId());
}
//测试是否修改性别为注释默认值
@Test
public void test2() throws IllegalAccessException {
Student student = new Student("李四", "20210635128634", "女");
Student.modifyGender(student);
System.out.println(student.toString());
}
}
test1输出:
stu_name = 张三 id: 20240101023505
test2输出:
Student{stuName=‘李四’, stuId=‘20210635128634’, gender=人妖}
总结
大家可以模仿我的案例自行测试,最好亲手做一遍!!!
框架 = 注解 + 反射 + 设计模式(我们一定要掌握基本的注释类原理)