注解

本文最后更新于:2023年1月20日 下午

注解

什么是注解?

注解是JDK5引入的一种注释机制,Java语言中的类、构造器、方法、成员变量、参数等都可以被注解进行标注。

注解有什么用?

作用:对Java中类、方法、成员变量做标记,然后进行特殊处理,至于到底做何种处理由业务需求来决定。例如在JUnit框架中,标记了注解@Test的方法就可以被当成测试方法执行,而没有标记的就不能当成测试方法执行。

如何使用注解?

注解的使用时在注解名前加上@符号,并在其后的括号中对各元素复制,如

@myAnnoation(value = "a")
public void method(){
    //...
}

预定义的注解

Java提供了一些预定义的注解类型,如@Deprecated@Override@SuppressWarnings 是在 java.lang 中预定义的。

@Deprecated 注解表示标记的元素已被弃用,不应再使用。只要程序使用带 @Deprecated 注解的方法,类或字段,编译器就会生成警告。

@Override 注解通知编译器该元素意图覆盖在超类中声明的元素。

@SuppressWarnings 注解告诉编译器抑制它将会生成的特定警告。在以下示例中,使用了不推荐使用的方法,编译器通常会生成警告。但是,在这种情况下,注解会导致警告被抑制。

// 使用已弃用的方法,并告诉编译器产生警告
@SuppressWarnings("deprecation")
void DeprecatedMethod() {
   objectOne.deprecatedMethod();
}

元注解

元注解也是注解,作用是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它annotation类型作说明。Java5.0定义的元注解有以下几种:

  1. @Target:约束自定义注解只能在哪些地方使用
  2. @Retention:申明注解的生命周期
  3. @Documented:表明可以将注解通过javadoc或类似工具进行文档化
  4. @Inherited:表明注解被自动继承

@Target中可使用的值定义在ElementType枚举类中,常用值如下:

可选值 使用范围
TYPE 类,接口
FIELD 成员变量
METHOD 成员方法
PARAMETER 方法参数
CONSTRUCTOR 构造器
LOCAL_VARIABLE 局部变量
PACKAGE

@Retention中可使用的值定义在RetentionPolicy枚举类中,常用值如下:

SOURCE 注解只作用在源码阶段,生成的字节码文件中不存在
CLASS 注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值
RUNTIME 注解作用在源码阶段,字节码文件阶段,运行阶段(开发常用)

使用示例:

@Target({ElementType.FIELD, ElementType.METHOD})//规定使用范围--变量和方法
@Retention(RetentionPolicy.RUNTIME)//注解作用在源码阶段,字节码文件阶段,运行阶段
@interface MyTest {
    //自定义注解
}

自定义注解

public @interface 注解名称 {
    public 属性类型 属性名() default 默认值;//默认值可以省略
}//属性类型:Java支持的数据类型基本上都支持

特殊属性:value属性,如果只有一个value属性的情况下,使用value属性的时候可以省略value名称不写!!

但是如果有多个属性, 且多个属性没有默认值,那么value名称是不能省略的。

示例:

@MyBook(name = "《精通JavaSE》", authors = {"小米", "大头"}, price = 9.9)
public class Test {//测试类

    @MyBook(authors = "Ahaha")
    public void Main() {

    }
}

@interface MyBook {
    //public 属性类型 属性名() default 默认值
    String name() default "nobody";

    String[] authors();

    double price() default 999.999;
}

解析注解

注解的解析就是判断是否存在注解,存在注解就解析出内容。

与注解解析的相关的接口

  1. Annotation:注解的顶级接口,注解都是Annotation类型的对象
  2. AnnotatedElement:该接口定义了与注解解析相关的解析方法,如下:
//获得当前对象上使用的所有注解,返回注解数组。
Annotation[] getDeclaredAnnotations();

//根据注解类型获得对应注解对象
T getDeclaredAnnotation(Class<T> annotationClass);

//判断当前对象是否使用了指定的注解,如果使用了则返回true,否则false
boolean isAnnotationPresent(Class < Annotation> annotationClass)

所有的类成分Class、Method、Field、Constructor,都实现了AnnotatedElement接口,都拥有解析注解的能力。

解析一个注解,先获取注解对象(使用反射提供的API),再获取对象中的属性(使用注解提供的API)

  1. 获取需要解析的对象
  2. 判断当前对象是否使用了指定的注解 isAnnotationPresent
  3. 获取注解 getDeclaredAnnotationgetDeclaredAnnotations

应用

模拟Junit框架

1、先自定义注解,表示junit中的 @Test

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyTest {
//content
}

2、书写测试方法,使用自定义的注解标注

@MyTest
public void test1() {
    System.out.println("==test1==");
}

3、在主方法的使用反射,获取该类中被注解标注的方法,并使用invoke执行

public static void main(String[] args) throws Exception {
    Annotation4 t = new Annotation4();
    Class<?> c = Annotation4.class;
    Method[] methods = c.getDeclaredMethods();
    for (Method method : methods) {
        if (method.isAnnotationPresent(MyTest.class)) {
            method.invoke(t);
        }
    }
}

这里的测试方法 和 主方法 是在同一个类中的

Lombok

@Data

使用这个注解,就不用再去手写Getter,Setter,equals,canEqual,hasCode,toString等方法了,注解后在编译时会自动加进去。

@AllArgsConstructor

使用后添加一个构造函数,该构造函数含有所有已声明字段属性参数

@NoArgsConstructor

使用后创建一个无参构造函数

@Builder

关于Builder较为复杂一些,Builder的作用之一是为了解决在某个类有很多构造函数的情况,也省去写很多构造函数的麻烦,在设计模式中的思想是:用一个内部类去实例化一个对象,避免一个类出现过多构造函数。


注解
http://agustletmen.github.io/2023/01/19/JavaSE-注解/
作者
Agust
发布于
2023年1月19日
更新于
2023年1月20日
许可协议