Java高级技术-注解

52 阅读5分钟

注解概述

●就是Java代码里的特殊标记,比如:@Override、@Test等,作用是:让其他程序根据注解信息来决定怎么执行该程序。

●注意:注解可以用在类上、构造器上、方法上、成员变量上、参数上、等位置处。

注解概述-注解的原理

image.png 注解本质是一个接口,Java中所有注解都是继承了Annotation接口的。

@注解(…):其实就是一个实现类对象,实现了该注解以及Annotation接口。

注解:自定义注解

介绍

自定义注解:就是自己定义注解。

格式:

public @interface 注解名称 {

    public 属性类型 属性名() default 默认值 ;

 }

@interface 就是 Java 中定义自定义注解(Annotation)的专属关键字—— 它的唯一用途就是声明一个注解类型,明确告诉编译器 “这是一个用于存储元数据的注解”,是自定义注解的 “标识性语法”。

注解接口中的成员方法默认就是 public 的,可以省略不写。

特殊属性名:value

如果注解中只有一个 value(特殊属性名)属性,使用注解时,value名称可以不写!!

此外注解中存在多个属性设置了默认值,并且只有当注解中所有未指定默认值的属性 “仅有 value(特殊属性名) 一个” 时,使用注解时,才能省略 value(特殊属性名)属性名称。

public @interface A{

// 方式1:带默认值
String name() default "";//可选属性,已经设置了默认值

// 当前代码(可以工作但不推荐)
//char name2() default 2;//整数字面量 2 会被自动转换为 char 类型,相当于 (char) 2,即隐式类型转换
//char name3() default '';//报错,字符一定要有一个字符并且只能有一个
// 推荐写法
//char name4() default '2';

// 方式2:不带默认值(使用时必须指定)
int age();//必选属性,未设置默认值

// 方式3:使用value作为特殊名称(可省略名称)
char value() default 'w';//可选属性,已经设置了默认值
    
}

必需属性:没有默认值的属性(如 age( ))- 必须在使用注解时指定

可选属性:有默认值的属性(如 name( )default " ")- 可以省略不写

@A(name = "",age = 22,value = 'w')
// 以上代码可以省略成以下代码
@A(age = 22)//可选属性name和value的属性值可以省略
// value(特殊属性名)简化赋值(多元素)
@MyAnnotation({"/a", "/b"}) // 等价于 value = {"/a", "/b"}
// 简化赋值(单元素)
@MyAnnotation("/a") // 等价于 value = {"/a"}

// 如果将int age();修改为int age() default 20;
// 同时将char value() default 'w';修改为char value();
// 满足多个属性有默认值,有却仅有一个value(特殊属性名)
// 注解就可以只写值不写名称
@A(22)

Java注解中可以定义的成员变量类型

  1. 基本数据类型
public @interface MyAnnotation {
    byte byteValue() default 0;
    short shortValue() default 0;
    int intValue() default 0;
    long longValue() default 0L;
    float floatValue() default 0.0f;
    double doubleValue() default 0.0;
    boolean booleanValue() default false;
    char charValue() default '\0';
}

2. 字符串类型

public @interface MyAnnotation {
    String value() default "";
    String name() default "default";
}

3. Class类型

public @interface MyAnnotation {
    Class<?> clazz() default Object.class;
    Class<? extends List> listType() default ArrayList.class;
}

  1. 枚举类型
enum Status { ACTIVE, INACTIVE }

public @interface MyAnnotation {
    Status status() default Status.ACTIVE;
}

  1. 注解类型
public @interface NestedAnnotation {
    String value() default "";
}

public @interface MyAnnotation {
    NestedAnnotation nested() default @NestedAnnotation("");
}

  1. 数组类型
public @interface MyAnnotation {
    String[] names() default {};
    //String[] name() default null; // 注解属性不能设为null
    int[] values() default {1, 2, 3};
    Class<?>[] classes() default {Object.class};
}

数组类型的赋值简化:当数组中只有一个元素时,可省略{},直接写单个值。
@MyAnnotation(names = "张三") // 合法,等同于 names = {"张三"}
@MyAnnotation(names = {"张三", "李四"}) // 多元素需用{}

7. 重要限制

成员变量不能是 null

成员变量不能是任意对象实例

成员变量必须在编译时就能确定值,同时注解属性的 “默认值必须是编译期常量”

不支持 void 类型

不支持复杂对象类型(除非是上述允许的类型)

注解:元注解

介绍

元注解:指的是:描述注解的注解。

//例如 : @Test上的注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Test {
}

常用注解

@Target

作用:声明被修饰的注解只能在哪些位置使用

@Target 注解是一个数组,可以为一个或多个目标元素类型同时指定注解的使用范围。

@Target(ElementType.TYPE) | |@Target({ElementType.TYPE,ElementType.METHOD})

1TYPE,类、接口(包括注解类型)、枚举类

2.FIELD,成员变量

3.METHOD,成员方法

4.PARAMETER,方法参数

5.CONSTRUCTOR,构造器

6LOCAL_VARIABLE,局部变量

@Retention

作用:声明注解的保留周期

@Retention(RetentionPolicy.RUNTIME)

1.SOURCE

只作用在源码阶段,字节码文件中不存在

2.CLASS(默认值)

保留到字节码文件阶段,运行阶段不存在

3.RUNTIME(开发常用)AnnotatedElement接口提供了解析注解的方法说明public Annotation[] getDeclaredAnnotations()获取当前对象上面的注解。public T getDeclaredAnnotation(Class annotationClass)获取指定的注解对象public boolean isAnnotationPresent(Class annotationClass)判断当前对象上是否存在某个注解

一直保留到运行阶段

注解的解析

就是判断类上、方法上、成员变量上是否存在注解,并把注解里的内容给解析出来。

如何解析注解?

指导思想:要解析谁上面的注解,就应该先拿到谁。

比如要解析类上面的注解,则应该先获取该类的Class对象,再通过Class对象解析其上面的注解。

比如要解析成员方法上的注解,则应该获取到该成员方法的Method对象,再通过Method对象解析其上面的注解。

Class 、Method 、Field ,Constructor、都实现了AnnotatedElement接口,它们都拥有解析注解的能力。

注解解析的前提条件:

  • 只有当注解的@Retention设置为RetentionPolicy.RUNTIME时,才能在运行时通过反射解析注解!
  • @RetentionSOURCE(源码阶段)或CLASS(字节码阶段),运行时getDeclaredAnnotations()会返回空数组,无法获取注解信息。
AnnotatedElement接口提供了解析注解的方法说明
public Annotation[] getDeclaredAnnotations()获取当前对象上面的注解
public T getDeclaredAnnotation(Class annotationClass)获取指定的注解对象
public boolean isAnnotationPresent(Class annotationClass)判断当前对象上是否存在某个注解