目录
1.Annotation的定义
注解(Annotation),也叫元数据,是一种代码级别的说明。
它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
其实Annotation就是代码里的特殊标记,它们可以在编译,类加载,运行时被读取,并执行相应的处理,以便于其他工具补充信息或者进行部署。
2.Annotation作用分类
- 编写文档:通过注解生成API文档(@Documented)
- 编译检查:通过注解让编译器实现基本的编译检查(@Override)
- 代码分析:通过注解对代码进行分析【反射等】(重点)
3.Annotation的语法形式
public @interface 注解名称 {}
我们可以定义一个简单的注解,如下:
package com.hanyxx.annotaion;
/**
* @author 菠菜饭团
* @description: 自定义注解
*/
public @interface bocai {
}
如果通过 javac命令将注解类编译,然后再通过 javap 命令进行反编译,就会发现: public interface com.hanyxx.annotaion.bocai extends java.lang.annotation.Annotation {}
注解的本质就是:接口
4.Annotation的分类
java内置了6个基本注解和4个元注解
4.1:6个基本注解
@Override :检测被该注解标注的方法是继承自父类(接口)的 @Deprecated:该注解标注的内容,表示已过时 @SuppressWarnings:压制警告 其中jdk1.7之后引入了另外两个基本注解: @SafeVarargs : Java7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告 @FunctionalInterface : Java8 开始支持,标识一个匿名函数或函数式接口。 @Repeatable: Java8 开始支持,标识某注解可以在同一个声明上使用多次
其中@Override、@Deprecated、@SuppressWarnings、@SafeVarargs和@FunctionalInterface在java.lang包下,而@Repeatable在java.lang.annotation包下
4.2:4个元注解
元注解(meta-annotation):定义注解的注解
4.2.1:@Target
@Target:用于描述注解能够作用的范围,可取的值存于ElementType枚举类中。
ElementType的取值范围:
取值 | 注解使用范围 |
TYPE | 可作 用于类或者接口上 |
FIELD | 可作用于成员变量上 |
METHOD | 可作 用于方法上 |
ANNOTATION_TYPE(了解) | 可作 用于注解类型上(被@interface修饰的类型) |
CONSTRUCTOR(了解) | 可作 用于构造方法上 |
LOCAL_VARIABLE(了解) | 可作 用于局部变量上 |
PACKAGE(了解) | 可用作 用于记录java文件的package信息 |
PARAMETER(了解) | 可作 用于参数上 |
TYPE_PARAMETER(了解) | 可用于标注类型参数(jdk 1.8后支持) |
TYPE_USE(了解) | 可用于标注任何类型名称(jdk 1.8后支持) |
4.2.2:@Retention
@Retention:表示在什么级别保存该注解信息。可取的值存于RetentionPolicy枚举类中。
RetentionPolicy的取值范围:
取值 | 注解使用范围 |
SOURCE | 注解将被编译器丢弃(该类型的注解信息只会保留在源码里,源码经过编译后,注解信息会被丢弃,不会保留在编译好的class文件里) |
CLASS | 注解在class文件中可用,但会被VM丢弃(该类型的注解信息会保留在源码和class文件里,在执行的时候,不会加载到虚拟机中),请注意,当注解未定义Retention值时,默认值是CLASS,如Java内置注解,@Override、@Deprecated、@SuppressWarnning等 |
RUNTIME | 注解信息将在运行期(JVM)也保留,因此可以通过反射机制读取注解的信息(源码、class文件和执行的时候都有注解的信息),如SpringMvc中的@Controller、@Autowired、@RequestMapping等。 |
4.2.3:@Documented
(了解)
@Documented:此注解会被javadoc工具提取成文档
4.2.4:@Inherited
(了解)
@Inherited:允许子类继承父类中的注解
5.Annotation的属性
从前面我们可以得知Annotation的本质是一个接口,既然是接口,那么自然能定义抽象方法。
我们把注解中的抽象方法称为注解的属性。
注解中属性的返回值类型有5种
- 基本数据类型
- String
- 枚举
- 注解
- 以上类型的数组
- 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。
- 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可。
- 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略
5.1简单演示
package com.hanyxx.annotaion;
import java.lang.annotation.*;
/**
* @author 菠菜饭团
* @description: 自定义注解
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface BoCai {
String name() default "菠菜饭团";
ElementType[] value() default ElementType.TYPE ;
String className();
String methodName();
}
需求:通过注解的形式创建任意对象,并且执行任意方法:
package com.hanyxx.annotaion;
import java.lang.reflect.Method;
/**
* @author 菠菜饭团
* @description: 通过注解的形式创建任意对象,并且执行任意方法
*/
@BoCai(className="com.hanyxx.annotaion.PlayGame",methodName = "play")
public class AnnotationTest {
public static void main(String[] args) throws Exception {
/**
* <A extends Annotation> A getAnnotation(Class<A> annotationClass) :如果存在该元素的指定类型的注解,则返回这些注解,否则返回 null。
* Annotation[] getAnnotations() : 返回此元素上存在的所有注解。
*/
//1.获取类字节码对象
Class<AnnotationTest> annotationClass = AnnotationTest.class;
//2.获取该类的注解对象(本质是在内存中生成该注解接口的子类实现对象)
BoCai boCai = annotationClass.getAnnotation(BoCai.class);
//3.获取传入的类名和方法名
String className = boCai.className();
System.out.println("获取类名:"+className);
System.out.println("===========================");
String methodName = boCai.methodName();
System.out.println("获取方法名:"+methodName);
System.out.println("===========================");
//4.加载类进内存,并获取方法对象
Class<?> Cls = Class.forName(className);
Method method = Cls.getMethod(methodName);
//5.创建对象实例并调用方法
Object o = Cls.newInstance();
method.invoke(o);
}
}
执行结果如下: