花个几分钟,带你再了解一下Java注解

createh51个月前 (02-14)技术教程7

注解(Annotation)是在java 1.5开始引入的,它是java中很重要的一个知识点。现在使用的很多框架,例如:SpringBoot、Hibernate等都大量的使用了注解。

注解是什么

注释是元数据的一种形式,提供有关程序的数据,而不是程序本身的一部分。注解对它们所注解的代码的操作没有直接影响。 它是Java中的一种特殊标记。

注解的用途

我们说注解的用途的前提是默认它是跟反射一起使用的,不然的话注解也就失去了它本来的作用了。注解有很多用途,比如:日志、权限、Spring中IOC等等都可以用注解来完成的。但是,总的来说有以下用途:

  • 编译器信息:可用于编译器检测错误或抑制警告。
  • 编译时和部署时处理: 软件工具可以处理注释信息以生成代码、XML文件等。
  • 运行时处理: 可以在运行时检查一些注解。
  • 生成文档:可以通过使用@Documented元注解,在需要的时候生成文档

标准注解

标准注解有10个,其中java.lang包下有5个分别是以下五个:

  1. 「@Override」: 检查该方法是否是重写了父类或者接口的方法,如果父类或者接口的方法删除或者被更改,编译器将提示报错。
  2. 「@Deprecated」:标记某个类、方法、属性等是否被弃用,在不推荐使用某个方法、类、属性等时,可以使用。
  3. 「@SuppressWarnings」:告诉编译器还可以忽略这个警告。
  4. 「@SafeVarargs」: Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
  5. 「@FunctionalInterface」:Java 8 开始支持,表明该接口是一个匿名函数或函数式接口。

元注解

元注解也是java标准注解,只不过它们比较特殊,是作用在注解上的注解。元注解都在java.lang.annotation包下。

  1. 「@Documented」:标记这些注解是否包含在用户文档中。
  2. 「@Target」:标记注解类型适用的java元素的种类。一些可能的值是 TYPE、METHOD、CONSTRUCTOR、FIELD 等。如果不存在目标元注释,则可以在任何程序元素上使用注释。
  3. 「@Inherited」 :表示允许子类继承父类中的注解。如果父类中使用了@Inherited注解,子类也就拥有了这个注解,子类通过Class对象的getAnnotation()方法就能获取到父类中的注解。
  4. 「@Retention」 :标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。它采用 RetentionPolicy 参数,其可能值为 SOURCE、CLASS 和 RUNTIME
  5. 「@Repeatable」:Java 8 开始支持,标识某注解可以在同一个声明上使用多次。

注解的分类

注解有5类,分别是:

  1. 标记注解:唯一的目的是标记一个声明。这些注释不包含任何成员,也不包含任何数据。@Override是标记注解的一个示例。
  2. 单值注解:顾名思义就是只有一个成员的注解,并且允许以简写形式指定成员的值。我们只需要在应用注解时指定该成员的值,不需要指定成员的名称。例如:@TestAnnotation(“测试”)
  3. 完整注解:由多个数据成员、名称、值、对组成的注解。例如:@TestAnnotation(owner=”公众号:索玛理”, value=”suncodernote”)
  4. 类型注解:可以应用于任何使用类型的地方的注解。例如,可以作为方法的返回类型。
@Target(value = ElementType.TYPE_USE)
public @interface TypeAnnoDemo {
}

public class AnnotationTest {
  public static void main(String[] args) {
      @TypeAnnoDemo String string = "局部变量被类型注解注解";
      type();
  }
  static @TypeAnnoDemo Integer type(){
      System.out.println("返回值被类型注解注解");
      return 0;
  }
}
  1. 重复注解:重复注解指的就是被 @Repeatable注解进行修饰的注解。

注解的定义

注解和接口的定义差不多,只不过注解多了个 「@」 符号。定义一个注解时有以下4点比较重要:

  1. 注解中的方法不能有参数。
  2. 注解中方法的返回值必须是基本数据类型、String、枚举、注解或者数组。
  3. 注解中的方法可以有默认值
  4. 注解上可以使用元注解

自定义注解

要自定义一个注解,有两个关键参数ElementType和RetentionPolicy必须要了解一下:

ElementType

ElementType是一个枚举类型,它做为数组在@Target注解中出现。作用是对Java程序中注解可能出现的语法位置进行简单分类。

public enum ElementType {
    TYPE,
    FIELD,
    METHOD,
    PARAMETER,
    CONSTRUCTOR,
    LOCAL_VARIABLE,
    ANNOTATION_TYPE,
    PACKAGE,
    TYPE_PARAMETER,
    TYPE_USE
}


类型 可以应用的地方

TYPE 作用于类、接口(包括注解类型)或者枚举类

FIELD 作用于属性

METHOD 作用于声明的方法

PARAMETER 作用于方法参数

CONSTRUCTOR 作用于构造方法

LOCAL_VARIABLE 作用于局部变量

ANNOTATION_TYPE 作用于其他注解

PACKAGE 作用于包

TYPE_PARAMETER java 1.8引入的,作用于泛型参数

TYPE_USE java 1.8引入的,作用于任何使用类型的地方

ElementType 中有些作用范围广的常量是可以替代其他常量的,就比如TYPE_USE可以可以替代PARAMETER 、LOCAL_VARIABLE 、FIELD等。

RetentionPolicy

注解的保留策略,在注解@Retention中声明,它表示要保留注解到哪种地步。

public enum RetentionPolicy {
    SOURCE,
    CLASS,
    RUNTIME
}
  • SOURCE:源码级别,编译期就会被抛弃用不到
  • CLASS:默认级别,编译期会被编译到类文件中,但在运行时获取不到。
  • RUNTIME:编译期会被编译到类文件中,运行时可以获取到,可以通过反射获取到。

小试牛刀

  1. 自定义一个注解CustomAnnotation
    ElementType枚举一共有10常量,这里就不一一测试了,感兴趣的自己可以来回测测。 自定义的注解想要在运行时获取到RetentionPolicy一定要设置成RUNTIME,否则找不到会报错。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE , ElementType.METHOD , ElementType.FIELD , ElementType.TYPE_PARAMETER, ElementType.LOCAL_VARIABLE , ElementType.PARAMETER , ElementType.TYPE_USE})
public @interface CustomAnnotation {
    String name() default "";

    /**
     * 在某个地方只有某个ElementType能起作用
     * @return
     */
    ElementType type() default ElementType.TYPE;

    /**
     * 能多个ElementType在同一个地方起作用的数组
     * @return
     */
    ElementType[] types() default {};
}
  1. 测试使用
    下面是对上面自定义注解@CustomAnnotation的使用,可以看到有的地方使用了types方法,有的使用了type方法。可以发现使用types 的地方都有ElementType.TYPE_USE的身影,java 8加的这个常量使用范围是真的广,能代替其他大部分常量了已经。
@CustomAnnotation(types ={ElementType.TYPE_USE , ElementType.TYPE} )
public class AnnotationTest<@CustomAnnotation(name = "泛型参数" , types ={ElementType.TYPE_USE , ElementType.TYPE_PARAMETER} ) T> {

    @CustomAnnotation(types ={ElementType.TYPE_USE , ElementType.FIELD})
    private String field;

    public static void main(String[] args) {
        @CustomAnnotation(types ={ElementType.TYPE_USE , ElementType.PARAMETER}) String string = "局部变量被类型注解注解";
        
    }

    static @CustomAnnotation(types ={ElementType.TYPE_USE , ElementType.METHOD} ) Integer type(){
        System.out.println("返回值被类型注解注解");
        return 0;
    }

    @CustomAnnotation(type = ElementType.METHOD)
    static void method(){
        System.out.println("作用在方法");
    }

    static void param(@CustomAnnotation(types ={ElementType.TYPE_USE , ElementType.PARAMETER}) Integer p){}
}

在自定义注解@CustomAnnotation中,将@Target元注解中的ElementType.TYPE_PARAMETER删除,测试类AnnotationTest不会报错,但是如果把ElementType.TYPE_USE , ElementType.TYPE_PARAMETER两个都删除的话,就会看到报错,这是因为 ElementType.TYPE_USE能代替ElementType.TYPE_PARAMETER。具体ElementType.TYPE_USE能代替几个ElementType枚举中的常量,感兴趣的话,自己可以动手测试一下。

  1. 获取注解
    前面说过注解要搭配着反射使用才有用,没有要注解也没啥大的用处。
static void getCustomAnnotation() throws NoSuchMethodException {

      AnnotationTest annotationTest = new AnnotationTest<>();
      final Class annotationTestClass = annotationTest.getClass();
      //获取类上的注解
      final CustomAnnotation classAnnotation = annotationTestClass.getAnnotation(CustomAnnotation.class);
      System.out.println("类上的注解"+Arrays.toString(classAnnotation.types()));

      //获取泛型上的注解
      final TypeVariable>[] typeParameters = annotationTestClass.getTypeParameters();

      for (TypeVariable> typeParameter : typeParameters) {
          final CustomAnnotation annotation = typeParameter.getAnnotation(CustomAnnotation.class);
          System.out.println("泛型上的注解:"+annotation);
      }

      //获取方法上的注解
      final Method[] declaredMethods = annotationTestClass.getDeclaredMethods();
      for (Method declaredMethod : declaredMethods) {
          final Annotation[] declaredAnnotations = declaredMethod.getDeclaredAnnotations();
          for (Annotation declaredAnnotation : declaredAnnotations) {
              System.out.println("方法名="+declaredMethod+",注解="+declaredAnnotation);
          }
      }
  }

结果:

总结

注解是Java中很重要的一个知识点,用起来也简单。它是类、方法、属性等的一个标记,搭配反射使用能够出奇效。

参考资料:

https://www.geeksforgeeks.org/annotations-in-java/

相关文章

jdk版本的选择(推荐1.8)_jdk版本哪个好

对java新手来说,选择jdk的版本也是个头晕的事情,今天小拿就给大家讲讲。内容包括1.jdk从1.5到1.11,选哪个最好2.jdk和jre的区别一、jdk版本选择jdk是java开发工具包,除了运...

Java 八股文_java八股文是什么意思

一、Java 基础知识1、Object 类相关方法getClass 获取当前运行时对象的 Class 对象。hashCode 返回对象的 hash 码。clone 拷贝当前对象, 必须实现 Clone...

关于Java8的精心总结

前言 最近公司里比较新的项目里面,看到了很多关于java8新特性的用法,由于之前自己对java8的新特性不是很了解也没有去做深入研究,所以最近就系统的去学习了一下,然后总结了一篇文章第一时间和大家分享...

Java 8 新特性探秘:Lambda 表达式、Stream API 和 Optional 的深度解析

一、引言Java 8 的出现为开发者带来了诸多强大的新特性,其中 Lambda 表达式、Stream API 和 Optional 更是备受瞩目。这些新特性极大地提高了编程效率和代码的可读性,为 Ja...

玩转 Java8 Stream,让你代码更高效紧凑简洁

文章目录前言一、Stream 特性二、Stream 创建2.1 用集合创建流2.2 用数组创建流2.3 Stream静态方法三、Stream 使用案例3.1 遍历 forEach3.2 过滤 filt...

window和linux安装JDK1.8_linux 安装jdk1.8 yum

Windows 安装 JDK 1.8 的步骤:步骤 1:下载JDK打开浏览器,找到JDK下载页面https://d.injdk.cn/download/oraclejdk/8在页面中找到并点击“下载...