注解的介绍
1、注解(Annotation)也被称为元数据(Metadata),用于修饰解释包、类、方法、属性、构造器、局部变量等数据信息。
2、和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在代码中的补充信息。
3)在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替java EE旧版中所遗留的繁冗代码和XML配置等。## ch03.注解
一、概念
注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
二、作用
1、编写文档:通过代码里标识的元数据生成文档【生成文档doc文档】
2、代码分析:通过代码里标识的元数据对代码进行分析【使用反射】
3、编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检【Override】
使用注解:@注解名称
在开发中,我们通过注解对源代码进行相应的标记,然后通过程序读取源代码相关的注解及其内容,进行解析(反射),实现相关的逻辑操作。
cmd控制台
# 帮助
javadoc -help
# 生成文档
javadoc -version -author MainTest.java
四、分类
1、按照来源划分
1)Java中常见的注解
- @Override:用于修饰此方法覆盖了父类的方法(而非重载)
- @Deprecated:用于修饰已经过时的方法
- @Suppvisewarnings:用于通知java编译器禁止特定的编译警告
2)部分第三方的注解
-
Spring
- @Autowired
- @Service
- @Repository
-
MyBatis
- @InsertProvider
- @UpdateProvider
- @Options
后期同学样学习框架时,会发现一般有两种操作,分别为xml配置和注解配置,注解的配置比xml的配置更简单、方便,所以注解很多情况都是为了替代xml。
注解在框架中起着非常重要的作用,它可以提供额外的元数据信息,帮助框架在运行时进行更加灵活、高效的处理。框架可以通过读取注解的元数据信息,来决定如何处理代码、调用相应的方法等等。
3)自定义注解
当只有一个属性是推荐使用value()
2、按照运行机制划分
- 源码注解:注解只在源码中存在,编译成.class文件就不存在了
- 编译时注解:注解在源码和.class文件中都存在,如:@override
- 运行时注解:在运行阶段还起作用,甚至会影响运行逻辑的注解,如:Autowired
这个也就是说,注解的有效范围(作用范围)
思考:程序员自定义的注解,它的有效范围应该是哪个呢? – 运行时(反射)
3、元注解
注解的注解,给注解进行注解,即在定义注解时,使用的注解。
也就是说,程序员在自定义注解时,对注解进行描述或说明的注解。
1)@Target
用于描述自定义注解使用位置
语法:@Target(ElementType.?)
- CONSTRUCTOR:构造方法声明
- FIELD:字段声明
- LOCAL_VARIABLE:局部变量声明
- METHOD:方法声明
- PACKAGE:包声明
- PARAMETER:参数声明
- TYPE:类、接口
注意:如果指定多个值,则需要加上大括号,如:
// 表示用户定义的注解可以应用在 类、接口、字段上面
@Target({ElementType.TYPE,ElementType.FIELD})
public @interface HelloWorld {
}
课堂练习:测试HelloWorld注解使用在接口上面、以及使用在某个方法参数上面。
2)@Retention
语法:@Retention(RetentionPolicy.?)
设置自定义注解的有效范围,有以下三个取值
- SOURCE:只在源码显示,编译时会丢弃
- CLASS:编译时会记录到class中,运行时忽略
- RUNTIME:运行时存在,可以通过反射读取
注意:一般自定义注解都设置有效范围为:RUNTIME,便于程序员在运行时能够解析注解(反射)
3)@Inherited
是一个标识注解,表示允许子类继承父类定义的注解
注意:只作用于类
4)@Documented
也是一个标识注解,设置生成javadoc时是否会包含注解
综合案例
// 定义一个HelloWorld注解
// 这个注解的生命周期为运行时,也就是在运行时可以通过反射获取到这个注解。
@Retention(RetentionPolicy.RUNTIME)
// 表示这个注解可以应用于类、方法和方法参数上。
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
// 表示该注解可以被继承。这意味着,如果一个注解被@Inherited修饰,并且被放在了某个类上面,那么该注解会自动被该类的子类所继承。
@Inherited
// 用于指定某个注解是否需要在JavaDoc中进行文档化。
@Documented
// 表示这是一个注解类型,名称为 HelloWorld
@interface HelloWorld {
// 表示这个注解有一个属性 value,其类型为字符串,默认值为 "Hello, World!"。
String value() default "Hello, World!";
// String value();
}
// 在接口上使用HelloWorld注解
@HelloWorld
abstract class MyInterface {
@HelloWorld("Hello, Annotation!")
abstract void myMethod();
}
// 实现接口并使用HelloWorld注解
class MyClass extends MyInterface {
@Override
@HelloWorld("Hello, Annotation!")
public void myMethod() {
System.out.println("MyClass.myMethod()");
}
}
public class Test {
public static void main(String[] args) {
MyInterface myClass = new MyClass();
// 输出注解的值
try {
HelloWorld helloWorld = MyClass.class.getAnnotation(HelloWorld.class);
System.out.println(helloWorld.value());
System.out.println(myClass.getClass().getDeclaredMethod("myMethod").getAnnotation(HelloWorld.class));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
五、语法
1、定义注解
@元注解
public @interface 注解名 {
数据类型 成员名称() [default 默认值];
// ....
}
说明:
1)@interface定义注解的关键字,是一个特殊的接口,但不是接口
2)成员以方法的形式声明,且不能有参数或抛出异常,但不是方法
3)成员可以使用default指定一个默认值
4)数据类型只能为
- 基本数据类型
- String
- Class
- Annotation
- Enumeration
- 以上类型的数组
注意:不能使用集合,如:Map
5)如果注解只有一个成员,则成员名可以取名为value(),在使用时可以忽略成员名和赋值号(=),而直接给定值就可以了
6)注解类可以没有成员,没有成员的注解称为标识注解
2、使用注解
@注解名称(成员名称=值,成员名称=值)
六、反射-操作注解
反射-操作注解中比较常用和方法
方法 | 使用类型 | 返回值 | 描述 |
---|---|---|---|
getAnnotations() | Class、Field、Method、Constructor、Package | Annotation[] | 返回该元素上所有直接存在的注解(包括继承而来的注解) |
getAnnotation(Class |
Class、Field、Method、Constructor、Package | T | 返回该元素上指定类型的注解(包括继承而来的注解),如果该类型注解不存在,则返回null |
isAnnotationPresent(Class<? extends Annotation> annotationClass) | Class、Field、Method、Constructor、Package | boolean | 判断该元素是否存在指定类型的注解 |
getDeclaredAnnotations() | Class、Field、Method、Constructor | Annotation[] | 返回该元素上所有直接存在的注解(不包括继承而来的注解) |
getDeclaredAnnotation(Class |
Class、Field、Method、Constructor | T | 返回该元素上指定类型的注解,如果该类型注解不存在,则返回null |
isAnnotationPresent(Class<? extends Annotation> annotationClass) | Class、Field、Method、Constructor、Package | boolean | 判断该元素是否存在指定类型的注解 |
第一:获取某个类的Class对象
第二:获取类的信息(Field、Method、Constructor、Parameter)
// 获取方法参数注解 - 返回一个二维数组
// 用于遍历方法所有的参数及对象的注解
// 其中,第一维是参数的个数,第二维是注解数组
Annotation[][] pa = 方法对象.getParameterAnnotations();
// 例子:
pa[0][0]:第一个参数的第一个注解
pa[0][1]:第一个参数的第二个注解
pa[1][0]:第二个参数的第一个注解
pa[1][1]:第二个参数的第二个注解
public int getSum(@A(100) @B(200) int n1,int n2) {
...
}
// 获取方法所有的参数对象
Parameter[] params = 方法对象.getParameters();
// 例子
params[0] : 第一个参数对象
params[1] : 第二个参数对象
注解名称 注解对象 = params[0].getAnnotation(注解名称.class) ;
第三:注解操作
1、判断是否存在注解
boolean bl = 对象.isAnnotationPresent(注解名称.class) ;
2、获取注解对象
注解名称 xxx = 对象.getAnnotation(注解名称.class);
3、获取注解的内容
数据类型 变量 = xxx.成员名称() ;
4、逻辑处理
根据需求