1. 什么是注解
1.1 概念
百度上的解释:
Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制,是一种代码级别的说明。Java 语言中的类、方法、变量、参数和包等都可以被标注。和 Javadoc 不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。 当然它也支持自定义 Java 标注。
概念描述:
- JDK1.5 之后的新特性
- 用来说明程序的
- 使用注解:@注解名称
1.2 作用分类
- 编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【Override】
- 编写文档:通过代码里标识的注解生成文档【生成文档doc文档】,API文档是通过抽取代码中的文档注释生成的。
- 代码分析:通过代码里标识的注解对代码进行分析【使用反射】
2. JDK中预定义的一些注解
- @Override : 检测被该注解标注的方法是否搜集继承自父类(接口)的
- @Deprecated :将该注解标注的内容,表示已过时
- @SuppressWarnings :压制警告,一般传递参数all @SuppressWarnings(“all”)
3. 自定义注解
3.1 格式
元注解:public @interface AnnotationName{}
3.2 本质
注解本质上就是一个接口,该接口默认继承Annotation
接口
1 2 3 4
| public interface MyAnno extends java.lang.annotation.Annotation{ returnType methodName1(); returnType methodName2() default value; }
|
3.3 属性
可以理解为接口中可以定义的抽象方法。
要求:
- 属性的返回值类型只能为以下几种:
- 基本数据类型
- String
- 枚举
- 注解
- 以上类型的数组
- 定义了的属性(本质上是抽象方法),在使用时需要进行赋值
- 格式:
(methodName1 = value1, methodName2 = value2)
- 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。
- 如果只有一个属性需要赋值,并且这个属性的名称是value,那么value可以省略,直接赋值即可。
- 数组赋值时,值使用大括号包裹。如果数组中只有一个值,那么{}可以省略
3.4 元注解
概念:用于描述注解的注解。
1 2 3 4 5 6 7 8
| @Target(value = {ElementType.TYPE}) public @interface MyAnno { }
其中value中的ElementType是一个枚举类型,取值可以有以下几种情况: - TYPE:可以作用在类上 - METHOD:可以作用在方法上 - FIELD:可以作用于成员变量上
|
4. 在程序中使用注解
注解在程序中经常和反射一起使用,注解大多数来说都是用来替换配置文件的,拿之前反射的程序来举例:
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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
|
public class AnnoTest1 { public void play(){ System.out.println("AnnoTest1 method play()"); } }
import java.io.InputStream; import java.lang.reflect.Method; import java.util.Properties;
public class ReflectTest { public static void main(String[] args) throws Exception {
Properties pro = new Properties(); ClassLoader classLoader = ReflectTest.class.getClassLoader(); InputStream is = classLoader.getResourceAsStream("pro.properties"); pro.load(is); String className = pro.getProperty("className"); String methodName = pro.getProperty("methodName"); Class cls = Class.forName(className); Object obj = cls.newInstance(); Method method = cls.getMethod(methodName); method.invoke(obj); } }
className=cn.other.annotation.AnnoTest1 methodName=play
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface AnnoReflect { String className(); String methodName(); }
import java.io.InputStream; import java.lang.reflect.Method; import java.util.Properties;
@AnnoReflect(className = "cn.other.annotation.AnnoTest1",methodName = "play") public class ReflectAnnotationTest { public static void main(String[] args) throws Exception {
Class<ReflectAnnotationTest> rac = ReflectAnnotationTest.class; AnnoReflect an = rac.getAnnotation(AnnoReflect.class);
String className = an.className(); String methodName = an.methodName(); Class cls = Class.forName(className); Object obj = cls.newInstance(); Method method = cls.getMethod(methodName); method.invoke(obj); } }
|
使用总结:
- 获取注解定义的位置的对象 (Class, Method, Field)
- 获取指定的注解:getAnnotation(Class)
- 调用注解中的抽象方法获取配置的属性值
5. 案例:简单的测试框架
需求:设计一个框架,检测一个类中的方法使用有异常,并进行统计。
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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
|
public class calculator { public void add(){ 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("今天天气真不错!"); } }
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Check {}
import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method;
public class TestCheck { public static void main(String[] args) throws IOException { calculator c = new calculator(); Class cls = c.getClass(); Method[] methods = cls.getMethods(); int num = 0; BufferedWriter bw = new BufferedWriter(new FileWriter("bug.txt")); for(Method method:methods){ if(method.isAnnotationPresent(Check.class)){ try { method.invoke(c); } catch (Exception e) { e.printStackTrace(); num++; bw.write(method.getName()+"方法出异常了"); bw.newLine(); bw.write("异常的名称是:"+e.getCause().getClass().getSimpleName()); bw.newLine(); bw.write("异常原因:"+e.getCause().getMessage()); bw.newLine(); bw.write("====================="); bw.newLine(); } } } bw.write("本次测试一共出现"+num+"次异常"); bw.flush(); bw.close(); } }
public class calculator { @Check public void add(){ System.out.println("1+0="+(1+0)); } @Check public void sub(){ System.out.println("1-0="+(1-0)); } @Check public void mul(){ System.out.println("1*0="+(1*0)); } @Check public void div(){ System.out.println("1/0="+(1/0)); } public void show(){ System.out.println("今天天气真不错!"); } }
|
小结 :
- 大多数时候,我们会使用注解而不是自定义注解
- 注解给编译器和解析程序用
- 注解不是程序的一部分,可以理解为标签
参考材料