什么是注解

注解:Annotation….
注解其实就是代码中的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相对应的处理。
比如有些配置文件xml配置了一些信息,可以直接写在注解里面,通过反射获取注解的信息。

注释是写给人看的,注解是写给程序看的,注解不影响程序的运行

自定义声明注解的写法

日常开发中新建Java类,我们使用class、interface比较多,而注解和它们一样,也是一种类的类型,他是用的修饰符为 @interface

  1. public @interface MyAnn {
  2. }

JDK自带的常用注解

  • @Overried

重写注解:一般使用IDE重写父类方法,这个注解就自己出来了。作用就是限定你这个方法是重写的父类的方法,如果这个方法不是重写的父类,编译错误

  • @Deprecated

过时注解:标注你写的这个方法已经过时了。在IDE里面一根删除线,但是这个方法还可以用。

  • @SuppressWarnings

抑制编译器警告:有时候编译器会警告你一些问题,不想看到警告就加上这个注解。
@SuppressWarnings(“unchecked”)要传值

元注解

元注解顾名思义我们可以理解为注解的注解,它是作用在注解中,方便我们使用注解实现想要的功能。

  • @Retention

Retention英文意思有保留、保持的意思,只能用于修饰其他的注解,用于指定被修饰的注解被保留多长时间。它表示注解存在阶段是保留在源码(编译期),字节码(类加载)或者运行期(JVM中运行)。在@Retention注解中使用枚举RetentionPolicy来表示注解保留时期

  1. RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
  2. RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期,运行时无法获得;
  3. RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在,在运行时可以通过反射获取到;
  • @Target

Target的英文意思是目标,这也很容易理解,使用@Target元注解表示我们的注解作用的范围就比较具体了,可以是类,方法,方参数变量等,同样也是通过枚举类ElementType表达作用类型

  1. ElementType.TYPE:作用接口、类、枚举、注解
  2. ElementType.FIELD:作用属性字段、枚举的常量
  3. ElementType.METHOD:作用方法
  4. ElementType.PARAMETER:作用方法参数
  5. ElementType.CONSTRUCTOR:作用构造器
  6. ElementType.LOCAL_VARIABLE:作用局部变量
  7. ElementType.ANNOTATION_TYPE:作用注解
  8. ElementType.PACKAGE:作用包
  9. ElementType.TYPE_PARAMETER:作用于类型泛型,即泛型方法、泛型类、泛型接口
  10. ElementType.TYPE_USE:可以用于标注任意类型除了 class
  • @Documented

Document的英文意思是文档。它的作用是 被该@Documented修饰的注解类将被javadoc工具提取成文档。用的很少

  • @Inherited

被这玩意修饰的注解具有继承性,比如@MyAnn注解被@Inherited修饰了,@MyAnn注解又修饰了Animal动物父类,但是Cat猫子类没有被@MyAnn修饰。因为@MyAnn注解被@Inherited修饰了,所以,Cat子类也具有了@MyAnn注解,子类继承父类的注解

  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Target({ElementType.METHOD,ElementType.FIELD,ElementType.CONSTRUCTOR})
  3. @Documented
  4. @Inherited
  5. public @interface MyAnn {
  6. }

如何使用注解

接着我们就可以在类或者方法上作用我们刚刚新建的注解。用@注解名放在声明类,接口,属性,方法等的上面。

目前这个注解毫无意义,因为注解里面什么都没有

  1. @MyAnn
  2. public class Student {
  3. String name;
  4. int age;
  5. @MyAnn
  6. private double score;
  7. @MyAnn
  8. public static void run(){
  9. System.out.println("学生跑步");
  10. }
  11. private int show(String a,int b){
  12. System.out.println(a+b);
  13. return b*2;
  14. }
  15. public Student(){
  16. }
  17. @MyAnn
  18. public Student(String name,int age){
  19. this.name=name;
  20. this.age=age;
  21. }
  22. }

注解的属性

注解的属性类型

注解属性类型可以有以下列出的类型

  1. 基本数据类型
  2. String
  3. 枚举类型
  4. 注解类型
  5. Class类型
  6. 以上类型的一维数组类型

    注解中的属性要加小括号,注解本质上是一个接口,可以设置默认值

  1. @Retention(RetentionPolicy.RUNTIME)
  2. public @interface MyAnn {
  3. String name();
  4. int age() default 32;
  5. }
  1. public class Student {
  2. @MyAnn(name = "名字")
  3. String name;
  4. @MyAnn(name = "名字",age=33)
  5. int age;
  6. private double score;
  7. public static void run(){
  8. System.out.println("学生跑步");
  9. }
  10. public Student(){
  11. }
  12. public Student(String name,int age){
  13. this.name=name;
  14. this.age=age;
  15. }
  16. }

value()属性

如果注解中只有一个value属性,使用注解时,不用指定属性,就能赋值。甚至value设置了默认值,可以不用传值。如果注解有多个属性,不管有没有value属性,使用时都要指定属性赋值

  1. @Retention(RetentionPolicy.RUNTIME)
  2. public @interface MyAnn {
  3. String value() default "默认值";
  4. }
  1. @MyAnn
  2. private double score;
  3. @MyAnn("value值")
  4. public static void run(){
  5. System.out.println("学生跑步");
  6. }

获取注解属性

获取注解的值才是注解的用途,不然光定义,根本没卵用。用反射。

  1. public class Student {
  2. @MyAnn(name = "名字")
  3. String name;
  4. @MyAnn(name = "名字",age=33)
  5. int age;
  6. private double score;
  7. public static void run(){
  8. System.out.println("学生跑步");
  9. }
  10. public Student(){
  11. }
  12. @MyAnn(name = "名字",age=33)
  13. public Student(String name,int age){
  14. this.name=name;
  15. this.age=age;
  16. }
  17. }
  1. public static void main(String[] args) {
  2. try {
  3. //反射一个类和它构造器,至于为什么要反射这个构造器,因为我知道这个构造器有注解,我要看程序知不知道
  4. Class aclass = Student.class;
  5. Constructor constructor = aclass.getConstructor(String.class,int.class);
  6. //判断是这个构造器是否有MyAnn注解
  7. boolean isAnn = constructor.isAnnotationPresent(MyAnn.class);
  8. if(isAnn){
  9. //获取这个构造器的注解
  10. //在构造器用getAnnotation获取的返回值是Annotation父类,而属性方法上用getAnnotation获取的是泛型注解,传入什么类型就是什么注解
  11. MyAnn annotation = (MyAnn) constructor.getAnnotation(MyAnn.class);
  12. //现在就获取到了值,至于这个值怎么操作,看自己
  13. System.out.println(annotation.name());
  14. System.out.println(annotation.age());
  15. //把注解值注入到构造器里面,实例化对象
  16. Student obj = (Student) constructor.newInstance(annotation.name(),annotation.age());
  17. System.out.println(obj.name);
  18. }
  19. } catch (NoSuchMethodException e) {
  20. e.printStackTrace();
  21. } catch (InvocationTargetException e) {
  22. e.printStackTrace();
  23. } catch (InstantiationException e) {
  24. e.printStackTrace();
  25. } catch (IllegalAccessException e) {
  26. e.printStackTrace();
  27. }
  28. }

总结

和jdbc配置文件那样,如果有些可能会改变的配置参数等,可以写在注解里面。封装一个反射取值注入的方法。
这样如果变更了参数,不用修改代码,只修改注解的值
处理提取和处理 Annotation 的代码统称为 APT(Annotation Processing Tool)

其实注解就像是一个标签,这个标签标注到,类,方法,字段上面。
那些框架很喜欢用注解,就是框架定义一些标签,开发者把这些标签打在类或者方法上,框架肯定有APT,等你程序运行或者编译的时候,就检查检查,这些类,方法是不是打上了我这个框架独有的标签,有标签就是一家人,没有?没有就不是一家人,不给你用框架的功能,还顺便给你报个错。
有注解就一定有反射,反射得到注解的一些信息才能做写什么,就像刷卡,卡里面有信息,刷卡才有意义,你他妈白卡刷什么?