注解
一、概念
Java提供了一种原程序中的元素关联任何信息和任何元素的途径和方法。
注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段(属性)、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
二、作用
- 编写文档:通过代码里标识的元数据生成文档【生成文档doc文档】
- 代码分析:通过代码里标识的元数据对代码进行分析【使用反射】
- 编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检【Override】
使用注解:@注解名称
在开发中,我们通过注解对源代码进行相应的标记,然后通过程序读取源代码相关的注解及其内容,进行解析(反射),实现相关的逻辑操作。
三、常见的注解
1.Java中常见的注解
- @Override:用于修饰此方法覆盖了父类的方法(而非重载)
- @Deprecated:用于修饰已经过时的方法
- @Suppvisewarnings:用于通知java编译器禁止特定的编译警告
2.第三方的注解
1)Spring
2)MyBatis
…
后期同学们学习框架时,会发现一般有两种小组,分别是xml配置和注解配置,注解的配置比xml的配置更简单、方便,所以注解很多情况都是为了替代xml。
# 帮助
javadoc -help
# 生成文档
javadoc -version -author MainTest.java
3)自定义注解
这章的重点,如何自定义注解,以及如何通过反射解析注解。
四、分类
1.按照运行机制划分
-
源码注解 :注解只在源码中存在,编译成.class文件就不存在了
-
编译时注解 :注解在源码和.class文件中都存在,如:@override
-
运行时注解 :在运行阶段还起作用,甚至会影响运行逻辑的注解,如:Autowired
这个也就是说,注解的有效范围(作用范围)
思考:程序员自定义的注解,它的有效范围应该是 ”运行时注解“ - 反射机制
2.按照来源划分
-
来自JDK的注解
-
来自第三方的注解
-
自定义注解
3.元注解
注解的注解,给注解进行注解,即在定义注解时,使用的注解。
也就是说,程序员在自定义注解时,对注解进行描述或说明的注解。
1)@target
用户描述自定义注解使用位置
语法:@Target(ElementType.?)
- CONSTRUCTOR:构造方法声明
- FIELD:字段声明
- LOCAL_VARIABLE:局部变量声明
- Method:方法声明
- PACKAGE:包声明
- PARAMETER:参数声明
- TYPE:类、接口
注意:如果指定多个值,则需要加上大括号,如:
// 表示用户定义的注解可以应用在类、接口、字段上面
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
pubblic @interface HelloWorld{
}
2)@Retention
语法:@Retention(RetentionPolicy.?)
- SOURCE:只在源码显示,编译时会丢弃
- CLASS:编译时会记录到class中,运行时忽略
- RUNTIME:运行时存在,可以通过反射读取
注意:一般自定义注解都设置有效范围为:RUNTIME,便于程序员在运行时能够解析注解(反射)。
3)@Inherited
标识注解,表示允许子类继承
不支持方法重写,即在父类中标注的方法,在子类重写时无效,不会被继承。它只作用于类。
4)@Documented
标识注解,设置生成javadoc时是否会包含注解。
五、自定义注解
1、语法
@元注解
public @interface 注解名 {
数据类型 成员() [default 默认值];
}
说明:
1)@interface定义注解的关键字,但不是接口
2)成员以方法的形式声明,且不能有参数或抛出异常,但不是方法
3)成员可以使用default指定一个默认值
4)数据类型只能为基本数据类型
- 基本数据类型(8种)
- String
- Class
- Annotation
- Enumeration(不能使用集合,如:Map)
- 以上类型的数组
注意:不能使用集合,如:Map
5)如果注解只有一个成员,则成员名可以取名为value(),在使用时可以忽略成员名和赋值号(=),而直接给定值就可以了
6)注解类可以没有成员,没有成员的注解称为标识注解
2、使用自定义注解
// 语法
@注解名[(成员名1=值1,成员名2=值2,…)]
说明:
1)值如果是一个数组,则使用”{}”,如:
@target({ElementType.METHOD,ElementType.TYPE}) // 默认成员名
@test(args={“arg1”,”arg2”})
2)如果定义注解时,只有一个value成员,则可以直接指定值,如:
@注解名(值)
3)使用注解时,注意使用范围,即target指定的作用域
六、反射解析注解
第一:获取某个类的Class对象
Class<类名> clazz = 类名.class;
第二:获取类的信息(Field、Method、Constructor、parameterType)
Field xxx = clazz.getDeclaredField("xx");
Method xxx = clazz.getDeclaredMethod("xx", int.class);
...
// 获取方法参数注解 - 返回一个二维数组
// 用于遍历方法所有的参数及对象的注解
// 其中,第一维是参数的个数,第二维是注解数组
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 b1 = 对象.isAnnotationPresent(注解名称.class);
2、获取注解对象
注解名称 xxx = 对象.getAnnotation(注解名称.class);
3、获取注解的内容
xxx.value();
数据类型 变量 = xxx.成员名称();
4、逻辑处理
根据需求
通过反射获取类、方法或成员上的运行时注解信息,从而实现动态控制程序运行的逻辑。
自定义注解
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description {
String value() ;
}
1、获取类中的注解
Class clz = Class.forName(“com.ann.test.Child”) ;
// 判断是否存在注解
boolean isExist = clz.isAnnotationPresend(Description.class) ;
if(isExist) {
// 获取注解的实例
Description d = (Description)clz.getAnnotation(Description.class) ;
System.out.println(d.value()) ;
}
2、获取方法的注解
1)方法一
Class clz = Class.forName(“com.ann.test.Child”) ;
Method[] ms = clz.getMethods() ;
for(Method m : ms) {
boolean isMExits = m.isAnnotationPresent(Description.class) ;
if(isMExits) {
Description d = (Description)m.getAnnotation(Description.class) ;
System.out.println(d.value()) ;
}
}
2)方法二
Class clz = Class.forName(“com.ann.test.Child”) ;
Method[] ms = clz.getMethods() ;
for(Method m : ms) {
Annotation[] as = m.getAnnotations() ;
for(Annotation a : as) {
if(a instanceof Description) {
Description d = (Description)a ;
System.out.println(d.value() ;) ;
}
}
}
3、获取字段的注解
Class<?> clazz = Student.class ;
// 1.获取字段
Field field = clazz.getDeclaredField("name") ;
// 2.判断字段是否存在某注解
boolean bl2 = field.isAnnotationPresent(Description.class) ;
System.out.println(bl2);
// 3.获取字段注解的值
Description desc = field.getAnnotation(Description.class);
System.out.println(desc.value());
// 4.遍历注解
Field[] declaredFields = clazz.getDeclaredFields();
for (Field declaredField : declaredFields) {
Description annotation = declaredField.getAnnotation(Description.class);
System.out.println(annotation.value());
}