Java注解(Annotation)基础知识

1. 基本介绍

注解(Annotation),也叫元数据,是JDK1.5引入的新特性。注解就是在代码中的特殊标记,这些标记可以在代码编译、加载、运行的时候被读取,并执行相应的处理。

注解在开发中是非常常见的,比如Junit、Spring框架提供的一系列注解,如@Controller、@Service等注解。除了框架实现的注解,Java也提供了许多基本注解。

2. 基本注解

在java.lang包下提供了5个基本注解: @Override, @Deprecated,@SuppressWarnings, @SafeVarargs,@FunctionalInterface .

2.1 @Override

@Override 注解的定义如下:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

该注解定义的位置是方法.标记方法属于重写父类的方法,这可以确保子类重写了父类的方法,避免出现低级错误。

例如,在IDEA中重写Object的toString 方法如果toString方法名拼写错误的话,就会报错.
IDEA会提示该方法不是重写父类的方法.
在这里插入图片描述

2.2 @Deprecated

@Deprecated的定义如下

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}

@Deprecated是用于标志已过时的类或方法等地方,当其他程序使用已过时的类或方法,方法时编译器会给出警告,建议不要使用.
例如:在IDEA中使用了过时的方法就会有中划线标注在这里插入图片描述

2.3 @SuppressWarnings

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}

@SuppressWarnings注解 用于抑制编译器警告,有选择的关闭编译器对类、方法、成员变量、变量初始化的警告。

被该注解修饰的元素以及该元素的所有子元素取消显示编译器警告.

value数组接收的值如下:

  • deprecation:使用了不赞成使用的类或方法时的警告;
  • unchecked:执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型;
  • fallthrough:当 Switch 程序块直接通往下一种情况而没有 Break 时的警告;
  • path:在类路径、源文件路径等中有不存在的路径时的警告;
  • serial:当在可序列化的类上缺少 serialVersionUID 定义时的警告;
  • finally:任何 finally 子句不能正常完成时的警告;
  • all:关于以上所有情况的警告。

2.4 @SafeVarargs

@SafeVarargs也是用来抑制编译器警告的注解。 Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。

2.5 @FunctionalInterface

@FunctionalInterface是用于声明函数式接口的.

什么是函数式接口?如果接口中只有一个抽象方法(可以包含多个默认方法或多个static方法)
接口体内只能声明常量字段和抽象方法,并且被隐式声明为public,static,final。
接口里面不能有私有的方法或变量

这个注解保证这个接口只有一个抽象方法,注意这个只能修饰接口

总结:
Java原生注解大多数用于“标记”和“检查”。有@Override、@Deprecated、@FunctionanlInterface。

3. 元注解

元注解(元Annotation )是用来修饰注解的,常见的元注解有@Retention和@Target

3.1 @Retention

@Retention的注解定义如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}

@Retention注解可以理解为设置注解的生命周期。@Retention注解传入的参数是RetentionPolicy枚举,该枚举有三个常量,分别是SOURCE、CLASS和RUNTIME。默认是CLASS.

RetentionPolicy注解定义与三个枚举常量的作用如下:

public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     * 注解只保留到源代码,编译器编译时会丢弃
     */
    SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     * 这是默认的行为, 注解会被记录到字节码文件中.但是不会在JVM运行时保留
     */
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *注解会被记录到字节码文件中.并且在JVM运行时保留,所以注解可以通过反射获取
     * 
     */
    RUNTIME
}

一般来说,如果要在编译期间处理注解相关的逻辑,只要自定义的注解中@Retention注解指定为CLASS级别,然后就需要继承AbstractProcessor并实现process方法来进行相关处理。比如Lombok的注解实现就用AnnotationProcessor继承了AbstractProcessor。

.java文件到class文件被JVM加载的过程如下图所示,其中的注解抽象语法树就是用于解析注解,然后做处理的逻辑。
在这里插入图片描述

一般来说,我们自己定义的注解都是RUNTIME级别的,因为大多数情况下是根据运行时环境去做一些处理的。而RUNTIME(运行时),是指Java文件经过javac编译后变成.class文件,然后放入到JVM,在JVM运行的期间

3.2 @Target

@Target注解定义如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    RetentionPolicy value();
}

@Target注解表示注解可以修饰在哪些程序单元,包括方法、成员变量、类等地方.接收传入参数ElementType枚举.ElementType定义的常量如下:

在这里插入图片描述

3.3 @Documented

@Documented注解用于指定被修饰的注解将被javadoc工具提取成文档,如果定义注解类时使用了这个注解修饰,则所有使用该注解修饰的程序单元,将会包含该注解说明。

3.4 @Inherited

@Inherited注解指定修饰的注解将具有继承性——如果某个类使用了注解@Xxx,则其子类将自动被注解@Xxx修饰

4. 自定义注解

4.1 基本介绍

自定义注解的格式如下:

元注解
public @interface 注解名称{
 属性列表
}

没有任何成员变量的注解称为标记注解。注解的本质就是一个接口,该接口默认继承Annotation接口

public interface MyAnnotation extends java.lang.annotation.Annotation {}

注解的属性定义格式就是相当于在接口中定义接口的抽象方法.属性的返回值类型有:

  • 基本数据类型
  • String
  • 枚举
  • 注解
  • (基本数据类型/String/枚举/注解)的数组类型

自定义注解的一些注意事项:

  1. 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。
  2. 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可。
    比如@SuppressWarnings注解
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}
  1. 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略

一般来说,我们自己定义的注解都是RUNTIME级别的,因为大多数情况下是根据运行时环境去做一些处理的,而Java反射是获取运行时信息的重要手段,所以我们平时开发中经常自定义注解配合Java反射机制来优化我们的代码。

4.2 注解相关的反射方法

  • boolean is AnnotationPresent(Class<?extends Annotation> annotationClass):判断该元素是否被annotationClass注解修饰
  • T getAnnotation(Class annotationClass):获取 该元素上annotationClass类型的注解,如果没有返回null
  • Annotation[] getAnnotations():返回该元素上所有的注解
  • T[] getAnnotationsByType(Class annotationClass):返回该元素上指定类型所有的注解
  • Annotation[] getDeclaredAnnotations():返回直接修饰该元素的所有注解
  • T[] getDeclaredAnnotationsByType(Class annotationClass):返回直接修饰该元素的所有注解

4.2 示例

使用反射技术将自定义注解的基本信息注入到对象上
1.自定义注解MyAnnotation

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    int id();

    String name();

    String gender() default "male";
}

2.在实体类上添加注解

@MyAnnotation(id = 1, name = "张三")
public class User {
    private int id;
    private String name;
    private String gender;
}    

3.获取注解的信息注入到对象中

public class AnnotationTest {
    public static void main(String[] args) {
        User user = new User();
        Class<? extends User> userClass = user.getClass();
        //通过反射得到类上的注解
        MyAnnotation myAnnotation = userClass.getAnnotation(MyAnnotation.class);
        int id = myAnnotation.id();
        String name = myAnnotation.name();
        String gender = myAnnotation.gender();
        //将注解的值注入到对象中
        user.setId(id);
        user.setName(name);
        user.setGender(gender);
        System.out.println(user);
    }
}

控制台输出:

User{id=1, name='张三', gender='male'}

拓展:
一个自定义注解配合Java反射机制的例子: 应用Java反射模拟实现Spring的IoC容器
深入学习注解:深入理解Java注解类型(@Annotation)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值