什么是注解
注解:Annotation….
注解其实就是代码中的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相对应的处理。
比如有些配置文件xml配置了一些信息,可以直接写在注解里面,通过反射获取注解的信息。
自定义声明注解的写法
日常开发中新建Java类,我们使用class、interface比较多,而注解和它们一样,也是一种类的类型,他是用的修饰符为 @interface
public @interface MyAnn {
}
JDK自带的常用注解
- @Overried
重写注解:一般使用IDE重写父类方法,这个注解就自己出来了。作用就是限定你这个方法是重写的父类的方法,如果这个方法不是重写的父类,编译错误
- @Deprecated
过时注解:标注你写的这个方法已经过时了。在IDE里面一根删除线,但是这个方法还可以用。
- @SuppressWarnings
抑制编译器警告:有时候编译器会警告你一些问题,不想看到警告就加上这个注解。
@SuppressWarnings(“unchecked”)要传值
元注解
元注解顾名思义我们可以理解为注解的注解,它是作用在注解中,方便我们使用注解实现想要的功能。
- @Retention
Retention英文意思有保留、保持的意思,只能用于修饰其他的注解,用于指定被修饰的注解被保留多长时间。它表示注解存在阶段是保留在源码(编译期),字节码(类加载)或者运行期(JVM中运行)。在@Retention注解中使用枚举RetentionPolicy来表示注解保留时期
- RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
- RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期,运行时无法获得;
- RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在,在运行时可以通过反射获取到;
- @Target
Target的英文意思是目标,这也很容易理解,使用@Target元注解表示我们的注解作用的范围就比较具体了,可以是类,方法,方参数变量等,同样也是通过枚举类ElementType表达作用类型
- ElementType.TYPE:作用接口、类、枚举、注解
- ElementType.FIELD:作用属性字段、枚举的常量
- ElementType.METHOD:作用方法
- ElementType.PARAMETER:作用方法参数
- ElementType.CONSTRUCTOR:作用构造器
- ElementType.LOCAL_VARIABLE:作用局部变量
- ElementType.ANNOTATION_TYPE:作用注解
- ElementType.PACKAGE:作用包
- ElementType.TYPE_PARAMETER:作用于类型泛型,即泛型方法、泛型类、泛型接口
- ElementType.TYPE_USE:可以用于标注任意类型除了 class
- @Documented
Document的英文意思是文档。它的作用是 被该@Documented修饰的注解类将被javadoc工具提取成文档。用的很少
- @Inherited
被这玩意修饰的注解具有继承性,比如@MyAnn注解被@Inherited修饰了,@MyAnn注解又修饰了Animal动物父类,但是Cat猫子类没有被@MyAnn修饰。因为@MyAnn注解被@Inherited修饰了,所以,Cat子类也具有了@MyAnn注解,子类继承父类的注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.FIELD,ElementType.CONSTRUCTOR})
@Documented
@Inherited
public @interface MyAnn {
}
如何使用注解
接着我们就可以在类或者方法上作用我们刚刚新建的注解。用@注解名放在声明类,接口,属性,方法等的上面。
目前这个注解毫无意义,因为注解里面什么都没有
@MyAnn
public class Student {
String name;
int age;
@MyAnn
private double score;
@MyAnn
public static void run(){
System.out.println("学生跑步");
}
private int show(String a,int b){
System.out.println(a+b);
return b*2;
}
public Student(){
}
@MyAnn
public Student(String name,int age){
this.name=name;
this.age=age;
}
}
注解的属性
注解的属性类型
注解属性类型可以有以下列出的类型
- 基本数据类型
- String
- 枚举类型
- 注解类型
- Class类型
- 以上类型的一维数组类型
注解中的属性要加小括号,注解本质上是一个接口,可以设置默认值
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnn {
String name();
int age() default 32;
}
public class Student {
@MyAnn(name = "名字")
String name;
@MyAnn(name = "名字",age=33)
int age;
private double score;
public static void run(){
System.out.println("学生跑步");
}
public Student(){
}
public Student(String name,int age){
this.name=name;
this.age=age;
}
}
value()属性
如果注解中只有一个value属性,使用注解时,不用指定属性,就能赋值。甚至value设置了默认值,可以不用传值。如果注解有多个属性,不管有没有value属性,使用时都要指定属性赋值
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnn {
String value() default "默认值";
}
@MyAnn
private double score;
@MyAnn("value值")
public static void run(){
System.out.println("学生跑步");
}
获取注解属性
获取注解的值才是注解的用途,不然光定义,根本没卵用。用反射。
public class Student {
@MyAnn(name = "名字")
String name;
@MyAnn(name = "名字",age=33)
int age;
private double score;
public static void run(){
System.out.println("学生跑步");
}
public Student(){
}
@MyAnn(name = "名字",age=33)
public Student(String name,int age){
this.name=name;
this.age=age;
}
}
public static void main(String[] args) {
try {
//反射一个类和它构造器,至于为什么要反射这个构造器,因为我知道这个构造器有注解,我要看程序知不知道
Class aclass = Student.class;
Constructor constructor = aclass.getConstructor(String.class,int.class);
//判断是这个构造器是否有MyAnn注解
boolean isAnn = constructor.isAnnotationPresent(MyAnn.class);
if(isAnn){
//获取这个构造器的注解
//在构造器用getAnnotation获取的返回值是Annotation父类,而属性方法上用getAnnotation获取的是泛型注解,传入什么类型就是什么注解
MyAnn annotation = (MyAnn) constructor.getAnnotation(MyAnn.class);
//现在就获取到了值,至于这个值怎么操作,看自己
System.out.println(annotation.name());
System.out.println(annotation.age());
//把注解值注入到构造器里面,实例化对象
Student obj = (Student) constructor.newInstance(annotation.name(),annotation.age());
System.out.println(obj.name);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
总结
和jdbc配置文件那样,如果有些可能会改变的配置参数等,可以写在注解里面。封装一个反射取值注入的方法。
这样如果变更了参数,不用修改代码,只修改注解的值
处理提取和处理 Annotation 的代码统称为 APT(Annotation Processing Tool)
其实注解就像是一个标签,这个标签标注到,类,方法,字段上面。
那些框架很喜欢用注解,就是框架定义一些标签,开发者把这些标签打在类或者方法上,框架肯定有APT,等你程序运行或者编译的时候,就检查检查,这些类,方法是不是打上了我这个框架独有的标签,有标签就是一家人,没有?没有就不是一家人,不给你用框架的功能,还顺便给你报个错。
有注解就一定有反射,反射得到注解的一些信息才能做写什么,就像刷卡,卡里面有信息,刷卡才有意义,你他妈白卡刷什么?