Joe1sn's Cabinet

JavaWeb笔记-注解

注释部分

注解

概念:说明程序。For计算机

从JDK1.5开始,Java增加对元数据的支持,也就是注解,注解与注释是有一定区别的,可以把注解理解为代码里的特殊标记,这些标记可以在编译,类加载,运行时被读取,并执行相应的处理。通过注解开发人员可以在不改变原有代码和逻辑的情况下在源代码中嵌入补充信息。

注释:用文字描述程序。For程序员

作用

  1. 编写文档:标识生成doc文档

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    package LearnJunit.annotation;
    /**
    * 注解 java doc演示
    * @since 1.5
    * @author joe1sn
    * @version 1.0
    */
    public class AnnoDemo1 {
    //计算两数之和
    public int add(int a, int b){
    return a+b;
    }
    }

    javadoc AnnoDemo1.java -encoding UTF-8 -charset UTF-8

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    正在加载源文件AnnoDemo1.java...
    正在构造 Javadoc 信息...
    标准 Doclet 版本 1.8.0_301
    正在构建所有程序包和类的树...
    正在生成.\AnnoDemo1.html...
    正在生成.\package-frame.html...
    正在生成.\package-summary.html...
    正在生成.\package-tree.html...
    正在生成.\constant-values.html...
    正在构建所有程序包和类的索引...
    正在生成.\overview-tree.html...
    正在生成.\index-all.html...
    正在生成.\deprecated-list.html...
    正在构建所有类的索引...
    正在生成.\allclasses-frame.html...
    正在生成.\allclasses-noframe.html...
    正在生成.\index.html...
    正在生成.\help-doc.html...

    image-20220317110445576

  2. 代码检查:代码分析(使用反射)

    例子: @Override

    也可以自定义注解【使用反射】

  3. 让编译器实现基本的检查

JDK预定义注解

  1. @Override

    编译检查,检查被标记方法是否是被父类覆写的方法

    image-20220316205850409

  2. @Deprecated

    该注解内容已过时

    image-20220317111415161

    image-20220317111514278

  3. @SuppressWarnings

    压制警告,需要传参,一般传递"all"压制所有警告

    image-20220317111629903

自定义注解及使用(解析)

格式

1
2
3
4
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
1
2
3
4
元注解
public @interface 注解名称{
属性列表
}

image-20220317205535179

本质:注解本质上就是一个接口,接口默认继承Annotation()

image-20220317205755742

1
2
public interface MyAnno extends java.lang.annotation.Annotation {
}

属性:接口中可以定义的成员方法

  • 要求:

    1. 属性返回类型

      • 基本数据类型
      • String
      • 枚举
      • 注解
      • 以上类型数组
      1
      2
      3
      4
      5
      6
      7
      public @interface MyAnno {
      int show1();
      String show2();
      Person per(); //枚举
      MyAnno2 anno2();//注解
      String[] strs();//字符串数组
      }
    2. 定义的属性在使用时需要给属性赋值

      缺少赋值:

      image-20220317210750659

      只留下show1进行赋值

      image-20220317210854025

      默认赋值

      1
      2
      3
      4
      public @interface MyAnno {
      int age();
      String name() default "Doe";
      }

      如果只有一个属性需要赋值,如果属性名称是value,直接赋值

      1
      2
      3
      4
      5
      6
      package LearnJunit.annotation;

      @MyAnno(1)
      public class Worker {
      }

      对于枚举、注解、字符串的注解

      1
      2
      3
      4
      5
      6
      7
      8
      @MyAnno(age=1,
      per=Person.P1,
      anno2=@MyAnno2,
      // strs={"1","a","v"}
      strs = "abcdefg"
      )
      public class Worker {
      }

元注解:用于描述注解的注解

  • @Target:描述注解能够作用的位置

    ElementType取值:

    • TYPE:作用于类上
    • METHOD:作用于方法上
    • FIELD:作用于成员变量上
    1
    2
    3
    4
    5
    6
    7
    8
    //表示anno3注解只能作用于类上
    @Target(value = {
    ElementType.TYPE,
    ElementType.METHOD,
    ElementType.FIELD
    })
    public @interface MyAnno3 {
    }
  • @Retention:描述注解能被保留的阶段

    RetentionPolicy:判断在哪个阶段,使用该注解

    1. SOURCE
    2. CLASS
    3. RUNTIME(一般使用这个):会保留到class字节码文件中,并被JVM读取到
  • @Documented:描述注解是否被抽取到api文档中

    1
    2
    3
    4
    5
    6
    @MyAnno3
    public class Worker {
    public String name = "John";
    @MyAnno3
    public void show(){};
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    /**
    *
    - @Target:描述注解能够作用的位置
    - @Retention:描述注解能被保留的阶段
    - @Documented:描述注解是否被抽取到api文档中
    - @Inherited:描述注解是否这子类继承
    */

    import java.lang.annotation.*;

    //表示anno3注解只能作用于类上
    @Target(value = {
    ElementType.TYPE,
    ElementType.METHOD,
    ElementType.FIELD
    })
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyAnno3 {
    }
    image-20220317213546637

    删除该注解后

    image-20220317213717359

  • @Inherited:描述注解是否被子类继承

解析注解

将配置文件的工作交给注解完成

1
2
3
4
5
6
7
8
9
/**
* 描述需要执行的类名和方法名
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Pro {
String className();
String method();
}
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
package LearnJunit.annotation;

import LearnJunit.reflect.ReflectDemo1;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

//假设框架类
@Pro(className = "LearnJunit.annotation.Demo1",
method = "show"
)
public class ReflectFramework {
public static void main(String[] args) throws Exception {
//1.解析注解
// 获取该类的字节码文件对象
Class<ReflectFramework> reflectFrameworkClass = ReflectFramework.class;
//2.获得注解对象
// 内存中生成了该注解接口的 子类实现对象
Pro an = reflectFrameworkClass.getAnnotation(Pro.class);
//3.调用注解对象中定义的抽象方法,获取返回值
String className = an.className();

}
}

关于步骤2,在 21行下个断点

image-20220318104234263

这个时候注解相关的都还没有对应数据,但是获得了注解(.getAnnotation)对象后

image-20220318104558428

不仅reflectFrameworkClass注解有了值,而且在内存中多出了一个an变量,而an就是我们注解的内容

最后加上

1
2
3
4
5
6
7
//4.反射获得类
Class aClass = Class.forName(className);
//5.获得类的方法
Method aMethod = aClass.getMethod(methodName);
//6.调用方法
Object aObject = aClass.newInstance();
aMethod.invoke(aObject);

就能使用注解中的类的对应方法了

image-20220318105703388

image-20220318105745302

案例

测试类

LearnJunit.annotation.demo.Calculator

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
package LearnJunit.annotation.demo;

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("always normal");
}
}

测试注解

LearnJunit.annotation.demo.check

1
2
3
4
5
6
7
8
9
10
11
package LearnJunit.annotation.demo;

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 {
}

测试方法

LearnJunit.annotation.demo.testCheck

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
package LearnJunit.annotation.demo;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Method;

/**
* 简单测试框架
* 挡住方法执行后,自动执行被检测的所有方法
*/
public class testCheck {
public static void main(String[] args) throws IOException {
//1.创建被测试对象
Calculator calculator = new Calculator();
//2.得到所有带注解的函数
int e_num = 0;//出现异常的次数
BufferedWriter bw = new BufferedWriter(new FileWriter("bug.txt"));//创建文件对象

//2.1得到所有函数
Method[] methods = calculator.getClass().getMethods();
for (Method method:methods){
//2.2检测是否带注解
if(method.isAnnotationPresent(check.class)){
//2.3若带注解则执行
try {
method.invoke(calculator);
} catch(Exception e){
//2.3.1捕获异常
e.printStackTrace();
//2.3.2记录到文件
e_num++;
bw.write("+++++"+method.getName()+" 方法异常");
bw.newLine();
bw.write("异常名称>> "+e.getCause().getClass().getSimpleName());
bw.newLine();
bw.write("异常原因>>"+e.getCause().getMessage());
bw.newLine();
}
}
}
bw.write("本次测试一共出现 "+e_num+" 次异常");
bw.newLine();
bw.flush();
bw.close();
}
}

image-20220318161130327

image-20220318161502996

小结

  1. 大多数时候使用注解而不是自定义注解
  2. 注解给谁用:
    • 编译器
    • 解析程序(checkTest)
  3. 注解不是程序的一部分(相当于C的编译参数,如预编译命令)