2022.4.20 注解

注解

注解 :JDK1.5 之后的新特性,说明程序的,给计算机看的。

注释:用文字描述程序的,给程序员看的。

作用分类

  • 编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【Override】

  • 编写文档:通过代码里标识的注解生成文档【生成文档doc文档】,API文档是通过抽取代码中的文档注释生成的。

  • 代码分析:通过代码里标识的注解对代码进行分析【使用反射】

内置注解

  • @Override : 检测被该注解标注的方法是否继承自父类(接口)的

  • @Deprecated :表示该注解标注的内容已过时

  • @SuppressWarnings :压制警告,一般传递参数all

    • @SuppressWarnings("all") 压制所有警告

自定义注解

格式:public @interface 注解名称{}

注解的本质

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

将以下注解编译过后进行反编译,得到结果:

编译前:

1 //自定义注解  @MyAnno
2 public @interface MyAnno{
3 4 }

反编译后:

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

注解中的属性

可以理解为接口中可以定义的抽象方法

要求:

  • 属性(抽象方法)的返回值类型只能为以下几种

    • 基本数据类型

    • String

    • 枚举

    • 注解

    • 以上类型的数组

  • 定义的属性(本质上是抽象方法),在使用时需要进行赋值,抽象方法的名称就是属性的名称,值的类型是抽象方法返回值类型

    • 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。

    • 如果只有一个属性需要赋值,并且这个属性的名称是value,那么value可以省略,直接赋值即可。

    • 数组赋值时,值使用大括号包裹。如果数组中只有一个值,那么{}可以省略

测试1

MyAnno

复制代码
 1 package com.xing.annotation;
 2  3  4 //自定义注解  @MyAnno
 5 public @interface MyAnno {
 6  7     // 抽象方法就是注解的一个属性   返回值有要求
 8     int age();
 9     String name();
10 11     // 可以设置默认值  这样在使用这个注解时就不用给这个属性赋值了
12     String str() default "";
13 }
复制代码

AnnotationTest

1 package com.xing.annotation;
2 3 // 这里应该是实现了age与name方法把值retuen回来了,使用注解相当于把方法调用了,str这个属性有默认值 可以不用赋值
4 @MyAnno(age = 12,name = "使用时给属性(抽象方法)赋值")
5 public class AnnotationTest {
6 }

测试2

MyAnno

1 package com.xing.annotation;
2 3 4 //自定义注解  @MyAnno
5 public @interface MyAnno {
6     String value();
7 }

AnnotationTest

1 package com.xing.annotation;
2 3 @MyAnno("小明")
4 public class AnnotationTest {
5 }

测试3

MyAnno

1 package com.xing.annotation;
2 3 4 //自定义注解  @MyAnno
5 public @interface MyAnno {
6     String[] names();
7 }

AnnotationTest

1 package com.xing.annotation;
2 3 @MyAnno(names = {"小明","小红"})
4 public class AnnotationTest {
5 }

元注解

元注解:用于描述注解的注解。Java定义了4个标准的meta-annotation类型,他们被用来提供对其他annotation类型作说明.

  • @Target():用于描述注解的使用范围(即:被描述的注解可以用在什么地方)

    复制代码
     1 //源码
     2 @Documented
     3 @Retention(RetentionPolicy.RUNTIME)
     4 @Target(ElementType.ANNOTATION_TYPE)
     5 public @interface Target {
     6     /**
     7      * Returns an array of the kinds of elements an annotation type
     8      * can be applied to.
     9      * @return an array of the kinds of elements an annotation type
    10      * can be applied to
    11      */
    12     ElementType[] value();
    13 }
    14 15 public enum ElementType {
    16     TYPE,
    17     FIELD,
    18     METHOD,
    19     PARAMETER,
    20     CONSTRUCTOR,
    21     LOCAL_VARIABLE,
    22     ANNOTATION_TYPE,
    23     PACKAGE,
    24     TYPE_PARAMETER,
    25     TYPE_USE
    26 }
    复制代码
    MyAnno 
    1 @Target(ElementType.TYPE) //表示该MyAnno注解只能作用于类上
    2 public @interface MyAnno {
    3 }

    AnnotationTest

    复制代码
    1 package com.xing.annotation;
    2 3 @MyAnno
    4 public class AnnotationTest {
    5 6     // @MyAnno()   报错不能作用在方法上
    7     // public void test() {
    8     // }
    9 }
    复制代码

    其中value中ElementType取值常用的几种情况:

    • TYPE:可以作用在类上

    • METHOD:可以作用在方法上

    • FIELD:可以作用于成员变量上

    1 @Target(value = {ElementType.TYPE,ElementType.METHOD,ElementType.FIELD}) 
    2 //表示该MyAnno注解可以同时作用于类上,方法上和成员变量上
    3 public @interface MyAnno {
    4 }
  • @Retention():表示需要在什么级别保存该注释信息,用于描述注解的生命周期

    复制代码
     1 //源码
     2 @Documented
     3 @Retention(RetentionPolicy.RUNTIME)
     4 @Target(ElementType.ANNOTATION_TYPE)
     5 public @interface Retention {
     6     /**
     7      * Returns the retention policy.
     8      * @return the retention policy
     9      */
    10     RetentionPolicy value();
    11 }
    12 13 14 public enum RetentionPolicy {
    15     SOURCE,
    16     CLASS,
    17     RUNTIME
    18 }
    复制代码
    • java代码三个阶段:源代码阶段 (SOURCE  注解会保留到java源文件阶段,class文件中不会保留) -> 类加载阶段 (CLASS   注解会保留到java源文件阶段,编译生成的class文件中也会保留,但是反射机制读取不到) -> 运行时阶段 (RUNTIME  注解会保留到java源文件阶段,编译生成的class文件中也会保留,反射机制会读取到)

    • @Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到class字节码文件中,并被JVM读取到,一般自己定义的注解都加RUNTIME

  • @Documented:表示将我们的注解生成在javadoc中

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

MyAnno

复制代码
 1 package com.xing.annotation;
 2  3 import java.lang.annotation.ElementType;
 4 import java.lang.annotation.Inherited;
 5 import java.lang.annotation.Target;
 6  7 @Target(ElementType.TYPE)
 8 @Inherited
 9 public @interface MyAnno {
10 }
复制代码

AnnotationTest

1 package com.xing.annotation;
2 3 @MyAnno
4 public class AnnotationTest {
5 6 }

Test

1 package com.xing.annotation;
2 3 4 //会继承@MyAnno这个注解  因为父类的注解@MyAnno被@Inherited描述了
5 public class Test extends AnnotationTest{
6 }

在程序中使用注解

注解在程序中经常和反射一起使用,注解大多数来说都是用来替换配置文件的,拿之前反射的程序来举例:

原配置文件测试

Student

复制代码
 1 package com.xing.annotation;
 2  3  4 import lombok.AllArgsConstructor;
 5 import lombok.Data;
 6 import lombok.NoArgsConstructor;
 7  8 @Data
 9 @AllArgsConstructor
10 @NoArgsConstructor
11 public class Student {
12     private String name;
13     private int age;
14 15     public void study(){
16         System.out.println("学生在学习!");
17     }
18 19 }
复制代码

pro.properties

1 className=com.xing.annotation.Student
2 methodName=study

MyReflectDemo

复制代码
 1 package com.xing.annotation;
 2  3 import java.io.InputStream;
 4 import java.lang.reflect.Constructor;
 5 import java.lang.reflect.Method;
 6 import java.util.Properties;
 7  8 /*
 9     反射可以跟配置文件结合的方式,动态的创建对象,并调用方法
10 */
11 public class MyReflectDemo {
12     public static void main(String[] args) throws Exception{
13 14         //1.读取配置文件中的信息
15         Properties prop = new Properties();
16         System.out.println(System.getProperty("user.dir"));//用户的当前路径
17 18         ClassLoader classLoader = MyReflectDemo.class.getClassLoader();
19         InputStream fis = classLoader.getResourceAsStream("pro.properties");
20         prop.load(fis);
21         fis.close();
22         System.out.println(prop);//{methodName=study, className=com.xing.annotation.Student}
23 24         //2.获取全类名和方法名  properties 中等号两边是键值对的形式
25         String className = (String) prop.get("className");
26         String methodName = (String) prop.get("methodName");
27 28         System.out.println(className);//com.xing.annotation.Student
29         System.out.println(methodName);//study
30 31         //3.利用反射创建对象并运行方法
32         Class<?> clazz = Class.forName(className);
33 34         //获取构造方法
35         Constructor<?> con = clazz.getDeclaredConstructor();
36         //通过构造方法创建这个对象
37         Object o = con.newInstance();
38         System.out.println(o);//Student(name=null, age=0)
39 40         //获取成员方法并运行
41         Method method = clazz.getDeclaredMethod(methodName);
42         method.setAccessible(true);
43         //o这个对象去调用method代表的方法
44         method.invoke(o);//学生在学习!
45 46 47     }
48 }
复制代码

用注解代替配置文件

Student

复制代码
 1 package com.xing.annotation;
 2  3  4 import lombok.AllArgsConstructor;
 5 import lombok.Data;
 6 import lombok.NoArgsConstructor;
 7  8 @Data
 9 @AllArgsConstructor
10 @NoArgsConstructor
11 public class Student {
12     private String name;
13     private int age;
14 15     public void study(){
16         System.out.println("学生在学习!");
17     }
18 19 }
复制代码

MyAnno

复制代码
 1 package com.xing.annotation;
 2  3 import java.lang.annotation.ElementType;
 4 import java.lang.annotation.Retention;
 5 import java.lang.annotation.RetentionPolicy;
 6 import java.lang.annotation.Target;
 7  8 @Target(ElementType.TYPE)
 9 @Retention(RetentionPolicy.RUNTIME)
10 public @interface MyAnno {
11     String className();
12 13     String methodName();
14 }
复制代码

AnnotationTest

复制代码
 1 package com.xing.annotation;
 2 
 3 
 4 import java.lang.reflect.Method;
 5 
 6 @MyAnno(className = "com.xing.annotation.Student",methodName = "study")
 7 public class AnnotationTest {
 8 
 9     public static void main(String[] args) throws Exception {
10 
11         //1. 解析注解
12         //1.1 获取该类的字节码文件对象
13         Class<AnnotationTest> rac = AnnotationTest.class;
14         //判断类上有没有该注解
15         if(rac.isAnnotationPresent(MyAnno.class)){
16                 //1.2 获取上面的注解对象,其实就是在内存中生成了一个该注解接口的子类实现对象 参数为注解名称
17                 MyAnno an = rac.getAnnotation(MyAnno.class);
18                 /*
19                 相当于
20                 public class MyAnno implements AnnoReflect{
21                     public String className(){
22                         return "com.xing.annotation.Student";
23                     }
24                     public String methodName(){
25                         return "study";
26                     }
27                 }
28                 */
29         }
30 
31         //2. 调用注解对象中定义的抽象方法,获取返回值
32         String className = an.className();
33         String methodName = an.methodName();
34         System.out.println(className);//com.xing.annotation.Student
35         System.out.println(methodName);//study
36 
37         //3.加载该类进内存
38         Class cls = Class.forName(className);
39         //4.创建对象
40         Object obj = cls.newInstance();
41         //5.获取方法对象
42         Method method = cls.getMethod(methodName);
43         //6.执行方法
44         method.invoke(obj);//学生在学习!
45     }
46 }
复制代码

 

使用总结:

在程序中使用注解:获取注解中定义的属性值

  • 获取注解定义的位置的对象 (Class, Method, Field)

  • 获取指定的注解:getAnnotation(Class)

  • 调用注解中的抽象方法获取配置的属性值

简单的测试框架

需求:设计一个框架,检测一个类中的方法是否有异常,并进行统计。

calculator

复制代码
 1 package com.xing.annotation;
 2  
 3  4 public class calculator {
 5  
 6     public void add(){
 7         System.out.println("1+0="+(1+0));
 8     }
 9  
10     public void sub(){
11         System.out.println("1-0="+(1-0));
12     }
13  
14     public void mul(){
15         System.out.println("1*0="+(1*0));
16     }
17  
18     public void div(){
19         System.out.println("1/0="+(1/0));
20     }
21     
22     public void show(){
23         System.out.println("今天天气真不错!");
24     }
25 }
复制代码

Check

复制代码
 1 package com.xing.annotation;
 2  3 import java.lang.annotation.ElementType;
 4 import java.lang.annotation.Retention;
 5 import java.lang.annotation.RetentionPolicy;
 6 import java.lang.annotation.Target;
 7  8 @Retention(RetentionPolicy.RUNTIME) // 运行时
 9 @Target(ElementType.METHOD) // 加在方法前面
10 public @interface Check {
11 }
复制代码

TestCheck

复制代码
 1 package com.xing.annotation;
 2  3 import java.io.BufferedWriter;
 4 import java.io.FileWriter;
 5 import java.io.IOException;
 6 import java.lang.reflect.Method;
 7  
 8 /** 简单的测试框架
 9  * 当主方法执行后,会自动自行检测所有方法(加了check注解的方法),判断方法是否有异常并记录
10  */
11 public class TestCheck {
12     public static void main(String[] args) throws IOException {
13         //1. 创建计算机对象
14         calculator c = new calculator();
15         //2. 获取字节码文件对象
16         Class<? extends calculator> cls = c.getClass();
17         //3. 获取所有方法
18         Method[] methods = cls.getMethods();
19  
20         int num = 0; //记录出现异常的次数
21         BufferedWriter bw = new BufferedWriter(new FileWriter("bug.txt"));
22  
23         for(Method method:methods){
24             //4. 判断方法上是否有Check注解
25             if(method.isAnnotationPresent(Check.class)){
26                 //5. 有注解就执行,捕获异常
27                 try {
28                     method.invoke(c);
29                 } catch (Exception e) {
30                     e.printStackTrace();
31                     //6.将异常记录在文件中
32                     num++;
33                     bw.write(method.getName()+"方法出异常了");
34                     bw.newLine();
35                     bw.write("异常的名称是:"+e.getCause().getClass().getSimpleName());
36                     bw.newLine();
37                     bw.write("异常原因:"+e.getCause().getMessage());
38                     bw.newLine();
39                     bw.write("=====================");
40                     bw.newLine();
41                 }
42             }
43         }
44         bw.write("本次测试一共出现"+num+"次异常");
45         bw.flush();
46         bw.close();
47  
48     }
49  
50 }
复制代码

在待测试的类中每个需要测试的方法前面都加上@Check

复制代码
 1 package cn.other.annotation.demo;
 2  
 3 /** 计算器类
 4  */
 5 public class calculator {
 6     @Check
 7     public void add(){
 8         System.out.println("1+0="+(1+0));
 9     }
10     @Check
11     public void sub(){
12         System.out.println("1-0="+(1-0));
13     }
14     @Check
15     public void mul(){
16         System.out.println("1*0="+(1*0));
17     }
18     @Check
19     public void div(){
20         System.out.println("1/0="+(1/0));
21     }
22  
23     public void show(){
24         System.out.println("今天天气真不错!");
25     }
26 }
复制代码

运行TestCheck类中的主方法,就会自动检查所有注解@Check的方法是否异常:

小结 :

  • 大多数时候,我们会使用注解而不是自定义注解

  • 注解给编译器和解析程序用

  • 注解不是程序的一部分,可以理解为标签

posted @   暴躁C语言  阅读(73)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)
点击右上角即可分享
微信分享提示