上一篇我们简单说了一下注解的入门,本篇我们了解一下注解的使用——自定义注解
我们首先定义一个简单的注解,只需要使用@interface 关键字来标识一下,他就成为了一个Person注解,这个注解的生命周期是到字节码阶段,使用范围是使用在类上面或者方法上面。
@Retention(RetentionPolicy.CLASS)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface Person {
}
在Annotation的实现中就看到了@Person注解,再一次印证所有注解都是基于Annotation实现
我们再写一个类,这个没有什么内容,只有一个@Person注解
@Person
public class PersonTest {
}
最后在写一个测试入口类
public class AnnotationTest {
public static void main(String[] args) throws Exception {
//通过反射获取到PersonTest的字节码类
Class clazz = Class.forName("com.lp.PersonTest");
//通过PersonTest的字节码类获取这个类上面的所有注解
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
//便利注解打印出注解的类型
Class<? extends Annotation> annotationType = annotation.annotationType();
System.out.println(annotationType);
}
}
}
我们运行这个测试类,发现什么都没有打印出来,按照我们的预期应该打印出一个@Person才对呀,那么问题出现在什么地方呢。。。其实我们定义@Person注解的时候指定了它的什么周期是到字节码的时候,当字节码加载到内存时这个注解就消失了,日然就打印不出来,那么我们把@Person 的生命周期指定到RUNTIME,再来测试一下,
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface Person {
}
打印结果如下, 通过annotationType方法我们获取了注解类型,这里主要强调两点,一个是注解的生命周期,再一个就是可以通过反射获取一个类的所有注解
下面我们给@Person注解加上两个属性,name和age
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface Person {
String name();
int age();
}
加上一个我们再看,PersonTest直接报错,因为没有给它的属性赋值
那么我们也可以给它指定默认值
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface Person {
String name() default "小李";
int age() default 8;
}
或者给它赋值以后就不报错了
@Person(name = "小明",age=10)
public class PersonTest {
}
下面我们通过测试类来获取它的属性,
public class AnnotationTest {
public static void main(String[] args) throws Exception {
//通过反射获取到PersonTest的字节码类
Class clazz = Class.forName("com.lp.PersonTest");
//通过PersonTest的字节码类获取这个类上面的所有注解
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
//遍历注解打印出注解的类型
Class<? extends Annotation> annotationType = annotation.annotationType();
//检验PersonTest上面的注解是否有Person注解
if (clazz.isAnnotationPresent(Person.class)){
//如果有,获取Person注解
Person personAnnotation = (Person) clazz.getAnnotation(Person.class);
//打印出属性
System.out.println("personAnnotation:"+personAnnotation.name()+"---"+personAnnotation.age());
}
System.out.println("annotationType:"+annotationType);
}
}
}
运行结果
注解属性类型可以是八个基本类型,String类型,Class类型,enum枚举类型,注解类型,前面这些类型的数组类型
下面我们把这些类型的属性都加上来获取它的属性值
public enum GradeEnum {
FirstGrade,SecondGrade,ThirdGrade
}
public @interface MetaAnnotation {
String value();
}
这个地方说一句,当一个注解中只有一个value属性时当使用的时候传参是可以不指定属性名称的,例如
MetaAnnotation annotation() default @MetaAnnotation("lp");就可以直接传参
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface Person {
String name() default "小李";
int age() default 8;
String[] pencilColor() default {"blue","red","yellow"};
GradeEnum grade() default GradeEnum.FirstGrade;
MetaAnnotation annotation() default @MetaAnnotation("lp");
Class clazz() default Person.class;
}
public class AnnotationTest {
public static void main(String[] args) throws Exception {
//通过反射获取到PersonTest的字节码类
Class clazz = Class.forName("com.lp.PersonTest");
//通过PersonTest的字节码类获取这个类上面的所有注解
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
//遍历注解打印出注解的类型
Class<? extends Annotation> annotationType = annotation.annotationType();
//检验PersonTest上面的注解是否有Person注解
if (clazz.isAnnotationPresent(Person.class)){
//如果有,获取Person注解
Person personAnnotation = (Person) clazz.getAnnotation(Person.class);
//打印出属性
System.out.println("personAnnotation:"+personAnnotation.name()+
"---"+personAnnotation.age()+
"---"+personAnnotation.annotation().value()+
"---"+personAnnotation.clazz()+
"---"+personAnnotation.grade().name()+
"---"+personAnnotation.pencilColor().length
);
}
System.out.println("annotationType:"+annotationType);
}
}
}
我们可以看到都可以获取出来,注解的基本定义就说到这里