zoukankan      html  css  js  c++  java
  • 注解

    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:

  • 相关阅读:
    微信公众号,图片预览功能 有单个图片,
    MUI
    MUI学习01-顶部导航栏
    php验证18位身份证,准到必须输入正确的身份证号,
    微信sdk 图片上传 两种方法 上传一张显示一张 并附带微信图片放大功能和删除功能
    jQuery实现限制input框 textarea文本框输入字符数量的方法
    js jq 手机号实现(344) 附带删除功能 jq 实现银行卡没四个数加一个空格 附带删除功能
    山东省2016acm省赛
    East Central North America Region 2015
    North America Qualifier (2015)
  • 原文地址:https://www.cnblogs.com/laurarararararara/p/11809127.html
Copyright © 2011-2022 走看看