• 注解


    Java 注解用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。Java 注解是从 Java1.5 开始添加到 Java 的。(比较抽象的概念)

    初学者可以这样理解注解:想像代码具有生命,注解就是对于代码中某些鲜活个体的贴上去的一张标签。简化来讲,注解如同一张标签。(引用某篇博主观点,通俗易懂!!)

    https://blog.csdn.net/shengzhu1/article/details/81271409

    下面创建了一个注解,那么注解的的使用方法是什么呢?

    @interface Test{
    }
    @Test()
    public class Cmath{
    }

    创建一个类 Cmath,然后在类定义的地方加上 @Test 就可以用 Testn 注解这个类了。

    你可以简单理解为将 Test 这张标签贴到 Camth这个类上面。

    不过,要想注解能够正常工作,还需要介绍一下一个新的概念那就是元注解。

    元注解: 注解的注解  

    元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其它的注解上面。

    如果难于理解的话,你可以这样理解。元注解也是一张标签,但是它是一张特殊的标签,它的作用和目的就是给其他普通的标签进行解释说明的。

    查看Target源码:   注解的作用目标(修饰方法、类、还是属性?) 

    常见的修饰:

    • ElementType.TYPE:允许被修饰的注解作用在类、接口和枚举上
    • ElementType.FIELD:允许作用在属性字段上
    • ElementType.METHOD:允许作用在方法上
    • ElementType.PARAMETER:允许作用在方法参数上
    • ElementType.CONSTRUCTOR:允许作用在构造器上
    • ElementType.LOCAL_VARIABLE:允许作用在本地局部变量上
    • ElementType.ANNOTATION_TYPE:允许作用在注解上
    • ElementType.PACKAGE:允许作用在包上
    @Retention(RetentionPolicy.RUNTIME)   
    @Target(ElementType.ANNOTATION_TYPE)  
    public @interface Target {
        /**
         * Returns an array of the kinds of elements an annotation type
         * can be applied to.
         * @return an array of the kinds of elements an annotation type
         * can be applied to
         */
        ElementType[] value();
    }

    查看Retention源码:注解的生命周期(保存到运行时还是编译时或者永久?)  默认注解保存到编译时期

    • RetentionPolicy.SOURCE:当前注解编译期可见,不会写入 class 文件
    • RetentionPolicy.CLASS:类加载阶段丢弃,会写入 class 文件
    • RetentionPolicy.RUNTIME:永久保存,可以反射获取(被保存在class字节码文件中,并被JVM读取)
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Retention {
        /**
         * Returns the retention policy.
         * @return the retention policy
         */
        RetentionPolicy value();
    }

    @Decumented:描述注解是否被抽取到api文档中

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

    注解的属性

    注解的属性也叫做成员变量。注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。

    @interface Test{
        String value();
        int id();
    }
    @Test(value ="数学",id=20)

    上面代码定义了 Test 这个注解中拥有 id 和 value两个属性。在使用的时候,我们应该给它们进行赋值。

    value这个参数名字,对所有注解来说,比较特殊,是默认的注解参数名字。

    赋值的方式是在注解的括号内以 value=" "形式,多个属性之前用 ,隔开。(有一个参数时,可以直接写"数学")

    @interface Test{
        String value() default "y";
        int id() default 4;
    }
    @Test()

    属性可以定义默认值default,定义默认值以后,Test里面可以不写参数

    注解属性的返回值类型(2020/1/21):

    1.基本数据类型 2.String  3.枚举 4.注解   5.以上类型的数组

     定义了属性后,使用的时候需要给属性赋值

    1.如果用了default关键字给属性默认初始化值,则使用该注解时,可以不进行属性的赋值

    2.如果只有一个属性,可以直接定义值

    3.数组赋值时,使用 { }包裹,如果数组中只有一个值,可以省略{ }

    注解的作用:

    注解到底有什么用?

    我们不妨将目光放到 Java 官方文档上来。

    文章开始的时候,我用标签来类比注解。但标签比喻只是手段,而不是目的。为的是让大家在初次学习注解时能够不被那些抽象的新概念搞懵。既然现在,我们已经对注解有所了解,我们不妨再仔细阅读官方最严谨的文档。

    注解是一系列元数据,它提供数据用来解释程序代码,但是注解并非是所解释的代码本身的一部分。注解对于代码的运行效果没有直接影响。

    注解有许多用处,主要如下:
    - 提供信息给编译器: 编译器可以利用注解来探测错误和警告信息
    - 编译阶段时的处理: 软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。
    - 运行时的处理: 某些注解可以在程序运行的时候接受代码的提取 (反射)

    常见的注解测试:Override  Deprecated   SuppressWarnning

    自定义注解练习

    练习1测试♥:找出所有方法的DevelopInfo注解(用于程序员的老大查看每个方法的开发人员与日期√,接口还是要注明!)

    import java.lang.annotation.*;
    import java.lang.reflect.Method;
    import java.util.Arrays;
    
    /**
     * 自定义注解类型   记录开发人员的  姓名String   开发日期String
     * value这个参数名字,对于所有注解来说,比较特殊,例如:
     * @DevelopInfo("张三") 就是把"张三"传给注解默认的参数名字了value
     * 也可以把参数的名字写出来,如@DevelopInfo(value = "张三")
     *
     */
    @Target({ElementType.METHOD}) //Target限制修饰的类型,说明DevelopInfo只能修饰方法
    @Retention(RetentionPolicy.RUNTIME) // 默认注解在编译时期存在,说明DevelopInfo注解信息可以保留到运行的时候
    @interface DevelopInfo{ // 用来修饰类,方法,变量,方法参数...
        String[] value() default "nobody"; // 注解的参数   参数名字叫 value
        String date();
    }
    
    /**
     * 新定义一个修饰方法的注解接口,可以保留到运行的时候
     */
    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @interface Check{
        int value();
    }
    
    class CMath{
        //@DevelopInfo(value = "张三", date = "2019-11-2")
        @DevelopInfo(value = {"张三", "李四"}, date = "2019-11-2")
        @Check(10)
        int sum(int a, int b){
            return a + b;
        }
    
        @DevelopInfo(value = {"高洋", "刘硕"}, date = "2019-10-2")
        int minor(int a, int b){
            return a - b;
        }
    
        @DevelopInfo(value = "张航", date = "2019-9-12") //数组中可以多个值{" "," "},也可以传入一个值 ""
        int div(int a, int b){
            return a / b;
        }
    
        @DevelopInfo(value = "吴雷", date = "2019-9-12")
        int mix(int a, int b){
            return a * b;
        }
    }
    public class 注解 {
        public static void main(String[] args) {
            /**
             * 通过Java反射,获取CMath这个类所有方法的注解开发信息
             */
            Class<CMath> c = CMath.class;
            /**
             * 获取所有方法的method
             * getField(只能public)     getDeclaredField(公有,私有,保护)
             */
            Method[] methods = c.getDeclaredMethods();
            for (int i = 0; i < methods.length; i++) {
                Annotation[] anns = methods[i].getDeclaredAnnotations();  //获取方法的注解
                if(anns != null){
                    // 遍历methods[i]当前方法的所有注解
                    for (int j = 0; j < anns.length; j++) {
                        if(anns[j] instanceof DevelopInfo){  //判断注解是否实现了DevelopInfo接口
                            DevelopInfo di = (DevelopInfo)anns[j];  //把注解类型转化为接口类型
                            System.out.println("方法名:" +
                                    methods[i].getName() +
                                    " 开发者:" + Arrays.toString(di.value()) +
                                    " 开发日期:" + di.date());
                            break;
                        }
                    }
                }
            }
        }
    }

     练习2  解析注解,获取注解中属性的值来获取某类的方法(同配置文件获取某类的方法)  配置文件获取某类的某方法

    主类:

    package test;
    import java.lang.reflect.Method;
    
    /**
     * @since 2020/1/21
     */
    @Pro(className = "test.Person",methodName = "person")
    public class ReflectTest3 {
        public static void main(String[] args) throws Exception {
            //提供一个模板(写一个框架类),可以创建任意类的对象,但是不能改变该类的对象的代码,但是不能改变框架的代码
    
            //1.解析注解:1.获取该类的字节码文件对象  2.获取注解
           Class<ReflectTest3> cls=ReflectTest3.class;
           Pro p= cls.getAnnotation(Pro.class);
           //2.调用注解对象中的属性(抽象方法),获得返回值
            String className=p.className();//获取了Pro注解的className属性的值,即"test.Person"
            String methodName=p.methodName();//获取了Pro注解的methodName属性的值,即"person"
            System.out.println(className);
            System.out.println(methodName);
            /**
             * 下面的步骤同配置文件应用反射的步骤,对获取到的类应用反射获取其方法
             * 好处:只需要改变该注解属性的值和就能获得不同类的不同方法
             */
            //3.加载该类进内存
            Class c=Class.forName(className);
            //4.创建对象
            Object obj= c.newInstance();
            //5.获取方法对象
            Method method=c.getMethod(methodName);
            //6.执行方法
            method.invoke(obj,null);
        }
    }

    Pro注解:

    package test;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * @since 2020/1/21
     * 该注解使用来描述执行的类名、方法名
     */
    @Target({ElementType.TYPE})//作用于类上
    @Retention(RetentionPolicy.RUNTIME)//保留在运行阶段
    public @interface Pro {
        String className();
        String methodName();
    }

     注:注解与配置文件原理相同,注解只需要改该类的注解中要获取的类和方法

     练习3   自动化测试♥        Junit单元测试例子

    开发人员写的代码:

    package Testtt;
    /**
     * 描述: 开发人员写好的功能代码
     *
     * @Author 
     */
    
    public class CMath {
        public int sum(int a, int b){
            return a + b;
        }
    
        public int minor(int a, int b){
            return a - b;
        }
    
        public int div(int a, int b){
            return a / b;
        }
    
        public int mix(int a, int b){
            return a * b;
        }
    }

    测试人员写测试接口进行测试:

    package Testt;
    
    import Testtt.CMath;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 描述: 测试用例类,测试人员写的
     * @Author hui
     */
    public class TestCaseUnit {
        @TestCase("CMath.sum")
        public boolean testsum(){
            CMath math = new CMath();
            int ret = math.sum(10, 20);
            return ret == 30;
        }
    
        @TestCase("CMath.minor")
        public boolean testminor(){
            CMath math = new CMath();
            int ret = math.minor(10, 20);
            return ret == -10;
        }
       @TestCase("Cmath.mix")
       public boolean testmix(){
           CMath math=new CMath();
           int ret=math.mix(10,20);
           return ret==100;
       }
    }
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @interface TestCase{
        String value();//存储测试的函数接口名称
    }

    注:写boolean类型的测试函数,并传入实际参数,通过每个测试的返回值测试开发人员写的代码;写TestCase接口,在主函数中通过注解来测试

    主函数类:

    package Testt;
    import java.lang.reflect.Method;
    
    /**
     * 描述:主类进行测试
     * @Author  hui
     */
    public class Mainn {
        public static void main(String[] args) throws Exception {
            /**
             * 编写代码,输出TestCaseUnit里面所有的测试用例结果
             * CMath.sum  测试结果:成功!  失败!
             */
            Class<TestCaseUnit> c = TestCaseUnit.class;
            Object obj = c.newInstance();
            Method[] methods = c.getDeclaredMethods();
            for (int i = 0; i < methods.length; i++) {
                TestCase tc = methods[i].getDeclaredAnnotation(TestCase.class);
                if (tc != null) {
                    System.out.print(tc.value() + " ");  //得到注解的内容
                    System.out.print("测试结果:");
                    if ((boolean) methods[i].invoke(obj)) {  .//通过反射调用测试类的方法,通过返回值确定函数是否正确
                        System.out.println("成功!");
                    } else {
                        System.out.println("失败!");
                    }
                }
            }
        }
    }

    注:通过注解得到测试人员写的测试函数的值(如: Cmath.add   测试结果:成功!),通过反射调用测试类的方法,输出每一个方法的测试结果,如果true,输出测试成功!

    结果:

    练习4:通过注解测试后将异常写入到一个文件中

    package AnnotationExample;
    /**
     * @since 2020/1/22
     */
    import java.io.BufferedWriter;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.lang.reflect.Method;
    public class CalculatorTest {
        public static void main(String[] args)throws IOException {
            Calculator c=new Calculator();
            Class cls=c.getClass();
            Method[] m=cls.getMethods();
            int count=0;
            //创建一个文件将异常导入到文件中
            BufferedWriter bw = new BufferedWriter(new FileWriter("bug.txt"));
            for(Method method:m){
                    if(method.isAnnotationPresent(Check.class)){
                        //如果有check注解,就执行该方法
                        try {
                            method.invoke(c);
                        } catch (Exception e) {
                            //将异常记录到文件中
                            count++;
                            bw.write(method.getName()+"方法出异常了");
                            bw.newLine();
                            bw.write("异常的名称:"+e.getCause());
                            bw.newLine();
                            bw.write("异常的原因:"+e.getCause().getMessage());
                            bw.newLine();
                            bw.write("--------------");
                            bw.newLine();
                        }
                    }
            }
            bw.write("本次测试一共出现"+count+"次异常");
            bw.flush();
            bw.close();
        }
    }

    被测试的Calculator类:

    package AnnotationExample;
    /**
     * @since 2020/1/22
     */
    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));
        }
    }

    另一种获取注解的方式:

    package AnnotationExample;
    /**
     * @since 20200/1/22
     */
    
    import java.io.BufferedWriter;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.lang.annotation.Annotation;
    import java.lang.reflect.Method;
    public class CalculatorTest2 {
        public static void main(String[] args)throws IOException {
            Calculator c=new Calculator();
            Class cls=c.getClass();
            Method[] m=cls.getMethods();
            int count=0;
            //创建一个文件将异常导入到文件中
            BufferedWriter bw = new BufferedWriter(new FileWriter("bug2.txt"));
            for(int i=0;i<m.length;i++){
                Annotation[] a=m[i].getDeclaredAnnotations();
                for(int j=0;j<a.length;j++) {
                    if (a[j] instanceof Check) {
                        //如果有check注解,就执行该方法
                        try {
                            m[i].invoke(c);
                        } catch (Exception e) {
                            //将异常记录到文件中
                            count++;
                            bw.write(m[i].getName() + "方法出异常了");
                            bw.newLine();
                            bw.write("异常的名称:" + e.getCause());
                            bw.newLine();
                            bw.write("异常的原因:" + e.getCause().getMessage());
                            bw.newLine();
                            bw.write("--------------");
                            bw.newLine();
                        }
                    }
                }
            }
            bw.write("本次测试一共出现"+count+"次异常");
            bw.flush();
            bw.close();
    
        }
    
    }

    注:获取注解应该是某方法的注解,不能写成该类的cls.getAnnotations( )

     bug.txt文件中同bug.txt2:

  • 相关阅读:
    为什么 PCB 生产时推荐出 Gerber 给工厂?
    Fedora Redhat Centos 有什么区别和关系?
    【KiCad】 如何给元件给元件的管脚加上划线?
    MCU ADC 进入 PD 模式后出现错误的值?
    FastAdmin 生产环境升级注意
    EMC EMI 自行评估记录
    如何让你的 KiCad 在缩放时不眩晕?
    KiCad 5.1.0 正式版终于发布
    一次单片机 SFR 页引发的“事故”
    java基础之集合
  • 原文地址:https://www.cnblogs.com/laurarararararara/p/11809127.html
Copyright © 2020-2023  润新知