? 注解,可以理解为标签,是一种特殊的“注释”,用来标识或解释Java代码,是给机器看的(而注释是给程序员看的)。
? 注解的定义:注解也叫元数据,跟类、接口、枚举是同一个层次的,也是java的一种类型,在Java SE 5.0开始引入,放在Java源码的类、方法、字段、参数前面,用来进行注释或说明。
注解的作用有以下三类:
标准注解
标准注解是指JDK自带的几个注解,主要有这几个:
@SuppressWarnings
元注解
元注解是用来修饰其他注解的,主要有以下几种:
@Retention:解释说明了注解的生命周期,有以下三种取值
RetentionPolicy.SOURCE
:注解只在源码阶段保留,在编译器进行编译时将注解丢弃或忽视RetentionPolicy.CLASS
:注解只保留到编译进行时,不会被加载进JVMRetentionPolicy.RUNTIME
:注解保留到程序运行时,会被加载进入JVM中,所以在程序运行时可以获取到它们@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
int value();
}
如果定义注解时
@Retention
不存在,则该Retention默认为RetentionPolicy.CLASS,但我们通常自定义的注解都是RUNTIME时使用,所以必须要加上@Retention(RetentionPolicy.RUNTIME
@Documented:用来将注解中的元素包含到Javadoc文档中
@Target:定义注解所修饰的对象范围,有以下7种取值
ElementType.PACKAE
:可以给一个包进行注解ElementType.TYPE
:可以给一个类型进行注解,比如类,接口,枚举等ElementType.CONSTRUCTOR
:可以给构造方法进行注解ElementType.METHOD
:可以给方法进行注解ElementType.PARAMETER
:可以给一个方法内的参数进行注解ElementType.FIELD
:可以给属性进行注解ElementType.LOCAL_VARIABLE
:可以给局部变量进行注解@Target(ElementType.METHOD) // 限制了注解MyAnnotation只能用于解释说明某个方法
public @interface MyAnnotation {
int value();
}
@Inherited:定义子类是否可以继承父类定义的注解,如果一个class使用了@Inherited修饰的注解,那么这个注解将被用于这个class的子类。
- 这个元注解只对类的继承起效,对接口的继承无效。
- 适用前提是:子类没有被任何注解应用。
// 元注解@Inherited 作用于 注解MyAnnotation
@Inherited
public @interface MyAnnotation {
int value();
}
// 注解MyAnnotation作用于 class A
@MyAnnotation
public class A {
}
// class B 继承了 class A,而且class B没有被其他注解应用
// 则class B继承了class A的注解@MyAnnotation
public class B extends A {
}
自定义注解
注解的定义格式:通过@interface关键字自定义
public @interface MyAnnotation {
}
// 这样就定义了一个注解了
注解本质上就是一个接口,该接口默认继承Annotation接口
public interface MyAnnotation extends java.lang.annotation.Annotation {}
注解的属性
注解的属性其实就是注解接口中的抽象方法,只是叫做属性,不叫方法
public @interface MyAnnotation {
int id();
String name();
}// 这个注解有两个属性:id 和 name
注解的属性是用无参的方法的形式来声明的,方法名就是属性名,比如上面的id和name,方法返回值就是属性的类型(下面介绍有多少种类型)
注解属性的类型
属性的类型(方法的返回值) 有以下几种取值:
注解属性的赋值
属性赋值的格式是:注解(属性名1=value1,属性名2=value2...),用逗号分隔。
在定义了属性之后,在使用注解的属性时需要赋值,有以下三种情况:
public @interface MyAnnotation {
int id() default 0;
String name();
}// 这个注解有两个属性:id 和 name
// 使用注解
@MyAnnotation(id=1,name="zhangsan")
public class A {
}
获取注解
获取某个对象上的所有注解,需要使用反射技术。
注意:反射的时间成本比较高,所以注解的使用需要慎重。
主要步骤有3个:
<T extends Annotation> getAnnotation(Class<T> annotationClass)
:返回指定类型的注解,如果不存在返回nullAnnotation[] getDeclaredAnnotations()
: 返回该元素上的所有注解还有一个API,这个方法在是Class类对象的成员方法,用于某个类是否应用了某个注解:
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
注解的获取
import java.lang.annotation.*;
/**
* 定义一个注解
* 注解在程序运行时使用需要加上@Retention(RetentionPolicy.RUNTIME)
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
// 定义两个属性
int id() default -1;
String type();
}
import java.lang.reflect.*;
/**
* 定义一个被注解MyAnnotation应用的类
*/
@MyAnnotation(id=1,type="Class")
public class ClassA {
// 注解作用于成员
@MyAnnotation(id=2,type="Field")
public int field;
// 注解作用于方法
@MyAnnotation(id=3,type="Method")
public void method() {
}
public static void main(String[] args) throws NoSuchFieldException, SecurityException, NoSuchMethodException {
// 1. 获取类上的注解
// 先判断类是不是应用了该注解
boolean flag = ClassA.class.isAnnotationPresent(MyAnnotation.class);
// 如果应用了,获取他的属性
if(flag) {
MyAnnotation annotation = ClassA.class.getAnnotation(MyAnnotation.class);
int id = annotation.id();
String type = annotation.type();
System.out.println("ClassA id: " + id);
System.out.println("ClassA type: " + type);
// 输出:
// ClassA id: 1
// ClassA type: Class
}
// 2. 获取成员变量上的注解
// 先利用反射获取成员变量field
Field f = ClassA.class.getField("field");
flag = f.isAnnotationPresent(MyAnnotation.class);
if (flag) {
MyAnnotation annotation1 = f.getAnnotation(MyAnnotation.class);
System.out.println("Field id: " + annotation1.id());
System.out.println("Field type: " + annotation1.type());
// 输出:
// Field id: 2
// Field type: Field
}
// 3.获取方法上的注解
Method m = ClassA.class.getMethod("method");
flag = f.isAnnotationPresent(MyAnnotation.class);
if (flag) {
MyAnnotation annotation2 = m.getAnnotation(MyAnnotation.class);
System.out.println("Method id: " + annotation2.id());
System.out.println("Method type: " + annotation2.type());
// 输出:
// Method id: 3
// Method type: Method
}
}
}
简单的测试小框架:测试类中的方法是否有问题
执行加了注解的方法,如果有bug,就输出到log文件中。
import java.lang.annotation.*;
/**
* 定义一个注解,用于方法上
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Check {
}
public class TestClass {
@Check
public void add(int a, int b) {
System.out.println("a + b = " + (a + b));
}
@Check
public void sub(int a, int b) {
System.out.println("a - b = " + (a - b));
}
@Check
public void div(int a, int b) {
System.out.println("a / b = " + (a / b));
}
}
import java.io.*;
import java.lang.reflect.*;
public class CheckClass {
public static void main(String[] args) throws IOException {
// 创建TestClass对象
TestClass tc = new TestClass();
// 获取Class对象
Class<?> cls = tc.getClass();
// 获取所有的方法
Method[] methods = cls.getMethods();
// 出错的次数
int errorNum = 0;
// log文件
BufferedWriter bw = new BufferedWriter(new FileWriter("log.txt"));
// 遍历所有的方法,有注解Check的方法执行
for (Method m : methods) {
// 判断是否有Check注解
if (m.isAnnotationPresent(Check.class)) {
// 执行方法
try {
m.invoke(tc, 1, 0);
} catch (Exception e) {
// 将异常信息写入log文件
errorNum++;
bw.write(m.getName() + "出现异常了");
bw.newLine();
bw.write("异常类型为:" + e.getCause().getClass().getName());
bw.newLine();
bw.write("异常原因为:" + e.getCause().getMessage());
bw.newLine();
bw.write("------------------------------------");
bw.newLine();
}
}
}
bw.write("本次测试一共出现了" + errorNum + "次异常");
bw.newLine();
bw.flush();
bw.close();
}
}
最后运行结果为:
$ java CheckClass
a + b = 1
a - b = 1
打开log.txt文件如下:
div出现异常了
异常类型为:java.lang.ArithmeticException
异常原因为:/ by zero
------------------------------------
本次测试一共出现了1次异常
原文:https://www.cnblogs.com/LucasBlog/p/12198887.html