概念


  • Java 注解是在​​JDK5​​​ 时引入的新特性,注解(也被称为​​元数据​​)
  • 为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便地使用这些数据
  • 注解类型定义指定了一种新的类型,一种特殊的接口类型
  • 在关键词​​interface​​​ 前加​​@​​​ 符号也就是用​​@interface​​ 来区分注解的定义和普通的接口声明


作用

提供信息给编译器


  • 编译器可以利用注解来探测错误和警告信息,如​​@Override​​​、​​@Deprecated​


编译阶段时的处理


  • 软件工具可以用来利用注解信息来生成代码、HTML 文档或者做其它相应处理
  • 如​​@Param​​​、​​@Return​​​、​​@See​​​、​​@Author​​​ 用于生成​​Javadoc​​ 文档


????doc 文档生成

 /**
* @author BNTang
**/
public class Client {
/**
* 登录
*
* @param userName 用户名
* @param password 密码
* @return boolean true(登录成功),否则为登录失败
*/
public boolean login(String userName, String password) {
if ("zs".equals(userName) && "1234".equals(password)) {
return true;
} else {
return false;
}
}
}

javadoc -encoding utf-8 .\Client.java

注解_赋值

注解_java_02

运行时的处理


  • 注解可以在程序运行的时候接受代码的提取
  • 通过代码里面标识注解对代码进行分析


JDK 中预定义的一些注解

@Override


  • 检测被该注解标注的方法是否是继承自父类 (接口) 的


@Deprecated


  • 该注解标注的内容,表示已过时


@SuppressWarnings


  • 压制警告
  • 一般传递参数​​all​​​,例如:​​@SuppressWarnings("all")​


/**
* @author BNTang
*/
@SuppressWarnings("all")
public class AnnotationDemo {

@Override
public String toString() {
return super.toString();
}

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

public void test2() {
// 如果没有SuppressWarnings会提示过期
test1();
new Date().toLocaleString();
}
}

自定义注解

基本格式

元注解
public @interface 注解名称 {
属性列表;
}
/**
* @author BNTang
*/
public @interface MyAnnotation {
}

本质


  • 拿到字节码进行反编译


javap .\MyAnnotation.class

注解_反射与注解_03

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


  • 注解本质上就是一个接口,该接口默认继承​​Annotation​​ 接口


属性列表

????接口中的抽象方法,返回值类型的要求,属性的返回值类型有下列取值


  • 基本数据类型
  • String
  • 枚举
  • 注解
  • 以上类型的数组


/**
* @author BNTang
*/
public @interface MyAnnotation {

int testOne();

String testTwo();

MyEnum test3();

MyAnnotationTest test4();

String[] test5();

// 报错, 不能是自定义类
// Login test6();

// 报错, 返回值不能为空
// void test7();
}

????定义了属性,在使用时需要给属性赋值


  • 如果定义属性时,使用​​default​​ 关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值


注解_反射与注解_04

注解_java_05


  • 如果只有一个属性需要赋值,并且属性的名称是​​value​​,则 value 可以省略,直接定义值即可


注解_java_06

注解_赋值_07


  • 数组赋值时,值使用​​{}​​​ 包裹。如果数组中只有一个值,则​​{}​​ 可以省略


注解_反射与注解_08

元注解

????什么是元注解


  • 用于描述注解的注解
  • ​JDK​​ 提供的注解,用于描述我们自己定义的注解


@Target


  • 作用:描述注解能够作用的位置


注解_反射与注解_09

????ElementType 取值


  • TYPE:可以作用于类上
  • METHOD:可以作用于方法上
  • FIELD:可以作用于成员变量上


注解_数组_10

/**
* @author BNTang
*/
@Target(value = {ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
public @interface MyAnnotation {
int age();

String[] name();
}

注解_反射与注解_11

/**
* @author BNTang
**/
@MyAnnotation(age = 10, name = "BNTang")
public class Person {
@MyAnnotation(age = 33, name = "JonathanLee")
private String name;

@MyAnnotation(age = 23, name = "my name is BNTang")
public void show() {

}
}

@Retention


  • 描述注解被保留的阶段
  • ​@Retention(RetentionPolicy.RUNTIME)​
  • 当前被描述的注解,会保留到​​class​​​ 字节码文件中,并被​​JVM​​ 读取到


注解_数组_12

注解_java_13

注解_赋值_14

@Documented


  • 描述注解是否被抽取到​​api​​ 文档中


@Inherited


  • 描述注解是否被子类继承


解析注解

????解析注解也就是使用注解当中传递的内容

使用步骤


  • 获取注解定义的位置的对象
  • 获取指定的注解


获取类上的注解

注解_数组_15

注解_数组_16

/**
* @author BNTang
**/
public class Client {
public static void main(String[] args) {
Class<Person> personClass = Person.class;

// 获取字节码上面的注解对象
MyAnnotation myAnnotation = personClass.getAnnotation(MyAnnotation.class);

String[] name = myAnnotation.name();
System.out.println(Arrays.toString(name));

int age = myAnnotation.age();
System.out.println(age);
}
}

注解_反射与注解_17

获取指定方法上的注解

/**
* @author BNTang
**/
public class Person {

private String name;

@MyAnnotation(age = 23, name = {"BNTang", "JonathanLee"})
public void show() {

}

public static void main(String[] args) throws NoSuchMethodException {
Class<Person> personClass = Person.class;

Method show = personClass.getMethod("show");

// 获取指定方法上面的注解对象
MyAnnotation myAnnotation = show.getAnnotation(MyAnnotation.class);

String[] name = myAnnotation.name();
System.out.println(Arrays.toString(name));

int age = myAnnotation.age();
System.out.println(age);
}
}

获取指定字段上的注解

/**
* @author BNTang
**/
public class Person {

@MyAnnotation(age = 23, name = {"BNTang", "JonathanLee"})
public String name;

public static void main(String[] args) throws NoSuchFieldException {
Class<Person> personClass = Person.class;

// 这里获取的字段是公共的获取私有的在反射文章当中有讲解不会的自行去参考即可
Field nameField = personClass.getField("name");

// 获取指定字段上面的注解对象
MyAnnotation myAnnotation = nameField.getAnnotation(MyAnnotation.class);

String[] name = myAnnotation.name();
System.out.println(Arrays.toString(name));

int age = myAnnotation.age();
System.out.println(age);
}
}


  • 内部动态的创建一个实现类,实现了​​MyAnnotation​​ 注解接口