在框架的使用过程中,常用到注解,本文对注解进行简单介绍。
注解
- 定义:注解(Annotation),也叫元数据。一种代码级别的说明。
- 它是
JDK1.5
及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。 - 作用分类:
- 编写文档:通过代码里标识的元数据生成文档【生成文档doc文档】
- 代码分析:通过代码里标识的元数据对代码进行分析【使用反射】
- 编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查【Override】
- 类比
- 注释:给程序员看
- 注解:给计算机看
1. 编译检查
- @Override:
2. 编写文档
- @author :作者
- @version:版本
- @since:
- @return:返回值
3. Java中预定义的注解
@Override
:检测该被该注解标注的方法是否是继承自父类(接口)@Deprecated
:表示该方法已经过时@SuppressWarnings()
:压制警告1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33package top.tobing.annotation;
/**
* Java内预定义的注解
* @author Tobing
* @version 1.0
* @since 1.5
*/
public class Demo01Annotation01 {
public static void main(String[] args) {
Cat cat = new Cat();
cat.run();
}
}
class Animal{
private String name;
public void eat() {
System.out.println("吃吃吃!!!");
}
}
// 压制所有的警告
class Cat extends Animal{
// 表示该方法是继承自父类的
public void eat() {
System.out.println("吃鱼!!!");
}
// 表示该方法已经过时,不建议使用
public void run() {
System.out.println("飞檐走壁!!!!");
}
}
4. 自定义注解
如何自定义呢?
照葫芦画瓢
— > 查看@Deprecated源码1
2
3
4
5
6
public Deprecated {
......
}以下是自定义的注解:
MyAnno.java
1
2
3package top.tobing.annotation;
public MyAnno {
}对自定义的注解反编译
1
2
3
4
5
6E:\Code>javac MyAnno.java # 编译获取字节码文件
E:\Code>javap MyAnno # 反编译字节码文件
Compiled from "MyAnno.java"
# 反编译得到的代码
public interface MyAnno extends java.lang.annotation.Annotation {
}从以上编译所的代码可以知道
注解的本质
本质:注解本质上就是一个接口,该接口默认继承了Annotation接口
public interface MyAnno extends java.lang.annotation.Annotation
- Annotation:The common interface extended by all annotation types.
- Annotation:所有注解类型拓展的公共接口。
注解属性:接口的抽象方法
- 要求:
- 属性返回值类型
- 基本数据类型
- String
- 枚举
- 注解
- 以上类型的数值
- 定义了属性,在使用时需要给属性赋值
- 定义属性时,使用default关键字给定默认值,使用注解时可以不进行赋值
- 只有一个属性需要赋值,并且名称是value,可以value可以省略,直接赋值即可。
- 数值赋值是,值需要用{}包裹。如果数组中只有一个值则可以省略{}
- 属性返回值类型
- 以下是代码演示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25package top.tobing.annotation;
// 自定义注解Anno
public MyAnno {
//int show(); // int
//String show2(); // String
//MyAnno1 anno1(); // 注解类型
String name();
String[] cats();
int[] ages();
int age() default 10; // 赋值了默认,可以不需要数值
}
// 自定义注解
MyAnno1{
String value(); // 只有一个值,且值的名称为value
}
//////////////使用注解//////////////////
// age由于定义了default,可以不赋值
// cats是数组,赋值多个的时候使用大括号包裹
// ages是数值,赋值一个的时候,可以省略大括号
// 省略了value=
public class Demo01Annotation2 {
}- 要求:
元注解
- 用于描述注解的注解(可以体会到元意思)
- @Target:描述注解的作用时机
ElementType
取值ElementType.TYPE
:可以作用在类上ElementType.METHOD
:可以作用在方法上ElementType.FIELD
:可以作用在成员变量上
- @Retention:描述注解被保留的阶段
- @Retention(保留阶段)
RetentionPolicy.RUNTIME
:保留到class字节码文件中,并且被JVM读取到(这个最常用)RetentionPolicy.SOURCE
:略RetentionPolicy.CLASS
:略
- @Documented:描述注解是否被抽取到api中
- @Inherited:描述注解是否被子类继承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// 以下元注解表述了:注解保留到class字节码文件中,并且被JVM读取到(这个最常用)
// 以下元注解表述了:注解可以被抽取到API中
// 以下元注解表述了:自定义注解可以被子类继承
// 以下元注解表述了:自定义注解可以被使用在类(TYPE)、方法(METHOD)、成员变量(FIELD)
MyAnno2{
String value();
}
package top.tobing.annotation;
public class Demo01Annotation3 {
private String name;
public void show() {
}
}
5. 注解的应用
1. 代替xml配置文件
传统的获取全限定类名,方法名,需要定义xml配置文件中,利用文件流获取xml,并将其中内容读取。操作比较麻烦。
使用注解可以代替此功能
- 自定义获取
className
methodName
的注解:Load.java
1
2
3
4
5
6
7
8
9
10
11
12package top.tobing.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
public Load {
String className();
String methodName();
}- 通过自定义注解获取
className
,methodName
,利用反射执行该方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37package top.tobing.annotation;
import java.lang.reflect.Method;
public class Demo04AnnotationReflect {
public static void main(String[] args) throws Exception {
// 获取本类字节码文件对象
Class<Demo04AnnotationReflect> demoClass = Demo04AnnotationReflect.class;
/**
此处本质上是获取了,内存中实现的Load接口的子类对象执行方法的返回值
public class LoadImpl{
public String className(){
return "top.tobing.annotation.Demo";
}
public String methodName(){
return "show";
}
}
*/
// 获取注解对象
Load load = demoClass.getAnnotation(Load.class);
// 获取注解的内容
String className = load.className();
String methodName = load.methodName();
System.out.println(className);
System.out.println(methodName);
// 通过类名以及方法名称利用反射,执行方法
//3.加载该类进内存
Class cls = Class.forName(className);
//4.创建对象
Object obj = cls.newInstance();
//5.获取方法对象
Method method = cls.getMethod(methodName);
//6.执行方法
method.invoke(obj);
}
}- 自定义获取
2. 自动以测试
要测试的类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28package top.tobing.annotation.test;
public class Cal {
//加法
public void add(){
String str = null;
str.toString();
System.out.println("1 + 0 =" + (1 + 0));
}
//减法
public void sub(){
System.out.println("1 - 0 =" + (1 - 0));
}
//乘法
public void mul(){
System.out.println("1 * 0 =" + (1 * 0));
}
//除法
public void div(){
System.out.println("1 / 0 =" + (1 / 0));
}
public void show(){
System.out.println("永无bug...");
}
}测试使用的注解
1
2
3
4
5
6
7
8
9
10package top.tobing.annotation.test;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
public MyCheck {
}测试类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30package top.tobing.annotation.test;
import java.lang.reflect.Method;
public class CalTest {
public static void main(String[] args) {
//1. 创建计算器对象
Cal c = new Cal();
//2. 获取计算器字节码文件
Class cls = c.getClass();
//3. 通过字节码文件获取使用方法
Method[] methods = cls.getMethods();
int num = 0;//记录异常出现的次数
for(Method m:methods) {
// 判断方法上是否包含注解
if(m.isAnnotationPresent(MyCheck.class)) {
// 包含,捕获异常
try {
m.invoke(c);
}catch(Exception e) {
num++;
System.out.println(m.getName()+"出现异常了");
System.out.println("异常名称为:"+e.getCause().getClass().getSimpleName());
System.out.println("异常原因是:"+e.getCause().getMessage());
System.out.println("----------------------");
}
}
}
System.out.println("本次测试一共出现"+num+"次异常");
}
}运行结果
1
2
3
4
5
6
7
8
9
10
11
12add出现异常了
异常名称为:NullPointerException
异常原因是:null
----------------------
1 - 0 =1
div出现异常了
异常名称为:ArithmeticException
异常原因是:/ by zero
----------------------
1 * 0 =0
本次测试一共出现2次异常
总结
- 在实战开发中,我们通常是使用注解,而并不是定义注解
- 此处复习注解是为了更好地学习框架。