- 概念:Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。
- 概念描述:
- 是一种注射机制
- 是JDK5.0新引入的
- 相比于注释:
- 注释,给人看
- 注解,给程序看
- 都不属于程序都一部分
- 都能用于生成JavaDoc
package com.steff;
/**
*Javadoc注解演示
*
* @author steff
* @since 2021.11.27
* @version 1.0
*/
public class Calc {
/**
*
* @param a 整数
* @param b 整数
*/
public int add(int a, int b){
return a + b;
}
}
-
作用分类:
- 编写文档,通过代码里标识的注解,生成文档【javadoc生成Doc】
- 编译检查,通过代码里标识的注解,让编译器能够实现基本都编译检查【Override】
- 代码分析,通过代码里标识的注解,对代码进行分析【使用反射】
-
JDK中定义的注解:
- Override:检测被该注解标注的方法是否是继承自父类的
- Deprecated:被该注解标注的,表示已过时
- SuppressWarnings: 编辑器中不展示警告
package com.steff;
/**
* JDK 中预定义的一些注解
* * Override:检测被该注解标注的方法是否是继承自父类的
* * Deprecated:被该注解标注的,表示已过时
* * SuppressWarnings: 编辑器中不展示警告
*/
public class Cat {
@Override
public String toString(){
return super.toString();
}
@Deprecated
public void meow(){
System.out.println("Meow~");
}
public void miaow(){
System.out.println("Miaow~");
}
public void speak(){
this.miaow();
this.meow();// IDE里面输入"this."后,候选的meow() 是有删除线的。
}
// 此方法会在编译器中被警告 "未被使用" / "void方法,无需return",
// 但是我们可以通过SuppressWarnings注解不让IDE提示
@SuppressWarnings("all")
public void mew(){
return;
}
}
我们可以看下@Override这个注解是怎么定义的:
package java.lang;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
- 自定义注解:
-
格式:
- 元注解 // 可选项{@Type, @Target, @Retention}
- public @interface 注解名称{}
-
按照格式,我们来自定义一个最简单的注解:
package com.steff; public @interface CustomAnnotation { }
-
注解原理揭示:
我们在 命令行 把CustomAnnotation.java编译为CustomAnnotation.class,
再将CustomAnnotation.class反编译,即可得到这个注解,再java中原本的样子。>javac CustomAnnotation.java >javap CustomAnnotation.class Compiled from "CustomAnnotation.java" public interface com.steff.CustomAnnotation extends java.lang.annotation.Annotation { }
反编译的结果告诉我们,注解本质其实就是一个默认继承了java.lang.annotation.Annotation的接口。
-
接下来我们再来看注解中属性
- 注解属性的类型有以下几种:
- 基本数据类型
- String
- 枚举
- 注解
- 以上类型组成的数组
package com.steff; @Target(ElementType.TYPE) public @interface CustomAnnotation { int time = 24*60*60; // 不了解定义普通变量有啥用,期待发掘 int times() default 7; String describe(); Color color(); Override an(); String[] note(); }
- 属性的使用和赋值:
- 使用注解时,需要给属性赋值,通过default关键字可以设默认值
- 只有一个属性,且属性名为value,可以直接赋值,不用指定参数名
- 数组赋值使用{}, 只有一个值时可以省略{}
package com.steff; @CustomAnnotation(describe="买可乐", color=Color.YELLOW, note="Let's 买可乐", an=@Override) public class Person { public void buyCola(){ System.out.println("买可乐"); } }
- 注解属性的类型有以下几种:
-
元注解 // 可选项
- @Target(ElementType.TYPE) //描述注解能作用的位置
TPYE -> 只能标注在类上
METHOD -> 只能标注在方法上
Field -> 只能标注在成员变量上? - @Retention(RetentionPolicy.RUNTIME) // 描述注解被保留的阶段
- RUNTIME -> 运行时
- CLASS -> 字节码
- SOURCE -> 源码
- @Documented // 描述注解是否被抽取到API文档中
- @Inherited // 描述注解是否被子类继承
- @Target(ElementType.TYPE) //描述注解能作用的位置
由于不知道这玩意有啥妙用,此处我们不再Demo元注解的用法,有需要可以单独研究
-
如何读取注解传入到数据?
package com.steff; import java.util.Arrays; @CustomAnnotation(describe="买可乐", color=Color.YELLOW, note="Let's 买可乐", an=@Override) public class Person { public static void main(String[] args) { // 获取被标注类的字节码文件 Class
personClass = Person.class; // 获取注解对象 CustomAnnotation customAnnotation = personClass.getAnnotation(CustomAnnotation.class); // 调用注解中的抽象方法,获取返回值 System.out.println(customAnnotation.describe()); System.out.println(Arrays.stream(customAnnotation.note()).iterator().next()); System.out.println(customAnnotation.color()); } } -
- 如何理解CustomAnnotation customAnnotation = personClass.getAnnotation(CustomAnnotation.class);
- 其实就是在内存中实现了该注解接口的子类实现,并重写了注解中的抽象方法,返回了传入的参数:
public class CustomAnnotationImpl implements CustomAnnotation{
@Override
public int times() {
return 0;
}
@Override
public String describe() {
return "买可乐";
}
@Override
public Color color() {
return Color.YELLOW;
}
@Override
public Override an() {
return null;
}
@Override
public String[] note() {
return "Let's 买可乐".split("");
}
@Override
public Class extends Annotation> annotationType() {
return null; // 这个不会编了
}
}
- 程序中使用注解
- 我们写个小练习,通过注解和反射测试Calc类中的方法。
package com.steff;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Check {
}
package com.steff;
public class Calc {
@Check
public void add(){
System.out.println("1 + 0 = " + (1 + 0));
throw new RuntimeException("你这个有Bug!");
}
@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("永无Bug...");
}
}
package com.steff;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Method;
/**
* 简单的测试框架
*
* 当主方法执行后,会自动进行被标记当所有方法(加了Check注解当方法)
* 监测异常,并记录到文件中
*
*/
public class TestCheck {
public static void main(String[] args) throws IOException {
// 1 创建 待测对象
Calc cal = new Calc();
// 2 获取 字节码文件对象
Class aClass = cal.getClass();
// 3 获取 所有待测方法
int count = 0; //出现异常的次数
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("Bug.txt"));
bufferedWriter.write("--------------------------------------");
bufferedWriter.newLine();
for (Method m : aClass.getMethods()) {
// 4 判断方法上是否有Check注解
if(m.isAnnotationPresent(Check.class)){
try {
// 5 执行有注解的方法
m.invoke(cal);
} catch (Exception e) {
// 6 异常记录
count++;
bufferedWriter.write(m.getName() + " 出现异常!");
bufferedWriter.newLine();
bufferedWriter.write("异常名称:" + e.getCause().getClass());
bufferedWriter.newLine();
bufferedWriter.write("异常原因:" + e.getCause().getMessage());
bufferedWriter.newLine();
bufferedWriter.write("--------------------------------------");
bufferedWriter.newLine();
}
}
}
// 7 测试结果
bufferedWriter.write("本次一共出现异常" + count + "次。");
bufferedWriter.flush();
bufferedWriter.close();
}
}
- 执行测试类主方法之后会在当前目录生成测试结果:
--------------------------------------
add 出现异常!
异常名称:class java.lang.RuntimeException
异常原因:你这个有Bug!
--------------------------------------
div 出现异常!
异常名称:class java.lang.ArithmeticException
异常原因:/ by zero
--------------------------------------
本次一共出现异常2次。