• Java进阶—Junit单元测试、反射、注解


    **

    一、Junit单元测试

    **

    测试分类:

    1.黑盒测试:不需要写代码,给输入值,看程序是否能够输出期望的值
    2.白盒测试:需要写代码,关注程序具体的执行流程。Junit是其中一种
    
    • 1
    • 2

    当我们遇到这样一种问题时:每一次只能测试一个方法,因为一个类里面只能写一个main方法,如果想测试多个方法,而且各个方法之间相互独立,则可以使用Junit解决。

    Junit使用步骤:

    1.定义一个测试类
    	编写格式建议:
    		测试类名:被测试的类名Test,例如CalTest
    		包名:xxx.xxx.test,最终放在test包中
    2.定义测试方法:可以独立运行
    	编写格式建议:
    		方法名:test测试的方法名,例如testAdd()
    		返回值:void,因为独立运行,不需要返回给调用处
    		参数列表:空参,因为独立运行,不需要返回给调用处
    3.给方法加上@Test注解,加上以后,就可以独立运行了
    4.导入Junit依赖环境,否则加上@Test之后会报错
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    判定结果:

    1.运行后显示红色:失败
    2.运行后显示绿色:成功
    一般会使用断言操作来处理结果:
    	Assert.assertEquals(期望的结果,程序运行的结果);
    
    • 1
    • 2
    • 3
    • 4

    注解:

    1.@Before:修饰的方法会在测试方法之前被自动执行
    	初始化方法:用于资源申请,所有测试方法在执行之前都会执行该方法。我们可以在一个方法前面加上注解@Before,使其成为一个初始化方法。
    2.@After:修饰的方法会在测试方法之后自动被执行
    	释放资源的方法:在所有测试方法执行完后,都会自动执行该方法。我们可以在一个方法前面加上注解@After,使其成为一个释放资源的方法
    
    • 1
    • 2
    • 3
    • 4

    注意事项:
    在单元测试中,一般不写输出,因为看输出不能判断程序是否正确,有输出只能说明代码语法正确,但是逻辑可能不正确。
    因此,我们一般使用断言操作来判断代码是否有问题,会将断言的结果和真是的结果比较,断言成功,运行后显示绿色;断言失败,运行后显示红色。

    **

    二、反射:框架设计的灵魂

    **
    2.1 框架

    框架是半成品软件。可以在框架的基础上进行软件开发,简化编码。
    写框架是需要反射的,框架之外的部分不需要反射技术。所以反射是框架的内部实现原理。

    2.2 反射

    将类的各个组成部分封装为其他对象,这就是反射机制。

    例如Class类对象,将

    成员变量封装成了Field[] fileds对象
    构造方法封装成了Constructor[] constructors对象
    成员方法封装成了Method[] methods对象
    
    • 1
    • 2
    • 3

    反射的好处:

    1.可以在程序运行过程中,操作这些对象。
    	例如String str = "abc";
    	str.写完(.)之后,会将字符串对象str所具有的的方法都提示出来,因为将所有的方法都封装到了Method[]数组中,这就是用到了反射技术,IDEA打开期间都可以提示,这就是程序运行时
    2.可以解耦,即降低程序的耦合性,提高程序的可扩展性
    
    • 1
    • 2
    • 3
    • 4

    2.3 有三种方式来获取Class对象,对应Java代码的三种阶段:

    1.Source源代码阶段:Class.forName("全类名")
    	将字节码文件加载进内存,返回Class对象
    	全类名即包名+类名
    	因为传递的是字符串,所以多用于配置文件,将类名定义在配置文件中,读取文件,加载类
    2.Class对象阶段:类名.Class
    	通过类名的属性class获取
    	多用于参数的传递
    3.Runtime运行时阶段:对象.getClass()
    	getClass方法是封装在Object类中的方法,所以所有对象都可以调用该方法
    	多用于对象的获取字节码的方式
    4.结论(重要!!!)
    	同一个字节码文件(.class)在一次程序运行过程中,只会被加载一次,不论通过哪种方式获取的Class对象都是同一个
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2.4 Class对象的功能——获取功能:

    1.获取成员变量们
        Field[] getFields():获取所有public修饰的成员变量
        Field[] getField(String name):获取指定名称的public修饰的成员变量
    
        Field[] getDeclaredFields():获取所有的成员变量,不考虑修饰符
        Field[] getDeclaredField(String name):获取指定名称的成员变量,需忽略访问权限修饰符的安全检查
    2.获取构造方法们
        Constructor[] getConstructors()
        Constructor[] getConstructor(类... parameterTypes)
    
        Constructor[] getDeclaredConstructors()
        Constructor[] getDeclaredConstructor(类... parameterTypes)
    3.获取成员方法们
        Method[] getMethods()
        Method[] getMethod(String name, 类... parameterTypes)
    
        Method[] getDeclaredMethods()
        Method[] getDeclaredMethod(String name, 类... parameterTypes)
    4.获取类名
        String getName()
    
    注意:带Declared的方法都需要忽略访问权限修饰符的安全检查
        setAccessible(true)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    Filed(成员变量):

    操作:
        1.设置值
            void set(Object obj, Object value)
        2.获取值
            Object get(Object obj)
        3.忽略访问权限修饰符的安全检查
            setAccessible(true):暴力反射
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    Constructor(构造方法):

    创建对象:
        T newInstance(Object... initargs)
    如果使用空参数构造方法创建对象,可以简化操作:使用Class对象的newInstance方法
    
    • 1
    • 2
    • 3

    Method(成员方法):

    执行方法:
        Object invoke(Object obj, Object... args)
    获取方法名称:
        String getName:获取方法名称
    
    • 1
    • 2
    • 3
    • 4

    2.5 Java代码在计算机中经历的三个阶段

    1)Source源代码阶段(.java --> .class)

    .java文件进经过javac编译之后,生成.class字节码文件,.class文件中主要包含的内容有:成员变量、构造方法、成员方法

    2)Class类对象阶段

    通过类加载器(ClassLoader)可以将Xxx.class(字节码文件)加载到内存中去。
    在内存中,会有一个对象来描述这个字节码问价,Java中有一个类叫做Class类,这个对象就是Class类对象,Class类就是用来描述字节码文件的一些共同行为和属性。

    Class对象中的主要部分:

    成员变量:封装为了Field[] fields对象
    构造方法:封装为了Constructor[] constructors对象
    成员方法:封装为了Method[] methods对象
    
    • 1
    • 2
    • 3

    3)Runtime运行时阶段

    我们通过Class对象的一些行为就可以创建真正的对象:new Person();

    2.6 写一个“框架”

    需求:

    不能改变该类的任何代码,可以创建任意类的对象,可以执行任意方法
    
    • 1

    技术:

    配置文件、反射
    
    • 1

    实现:

    1.将需要创建的对象的全类名和需要执行的方法定义在配置文件中
    2.在程序中加载读取配置文件
    3.使用反射技术来加载类文件到内存中
    4.创建对象
    5.执行方法
    
    • 1
    • 2
    • 3
    • 4
    • 5

    代码:

    public class ReflectTest {
        public static void main(String[] args) throws Exception {
            //可以创建任意类的对象,可以执行任意方法
    
            /*
                前提:不能改变该类的任何代码。可以创建任意类的对象,可以执行任意方法
             */
    
            //下述方法,在创建不同对象,执行其方法的时候,需要修改代码
    //        Person p = new Person();
    //        p.eat();
    
    //        Student stu = new Student();
    //        stu.sleep();
    
            //1.加载配置文件
            //1.1 创建Properties对象
            Properties prop = new Properties();
            //1.2 加载配置文件,转换为一个集合
            //1.2.1 获取文件的路径,即获取class目录下的配置文件:使用【类加载器】,后续可以将字节码文件加载进内存
            ClassLoader classLoader = ReflectTest.class.getClassLoader();
            InputStream is = classLoader.getResourceAsStream("pro.properties");
            prop.load(is);
    
            //2.获取配置文件中定义的数据
            String className = prop.getProperty("className");
            String methodName = prop.getProperty("methodName");
    
            //3.加载该类进内存
            Class cls = Class.forName(className);
    
            //4.创建对象
            Object obj = cls.newInstance();
            //5.获取方法对象
            Method method = cls.getMethod(methodName);
            //6.执行方法
            method.invoke(obj);
        }
    }
    
    • 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

    **

    三、注解

    **

    3.1 概念

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

    3.2 作用分类

    1)编写文档:通过代码里标识的元数据(注解)生成文档【生成文档doc文档】

    2)代码分析:通过代码里标识的元数据(注解)对代码进行分析【使用反射】

    3)编译检查:通过代码里标识的元数据(注解)让编译器能够实现基本的编译检查【override】如果是正确重写,则编译通过。

    3.3 如何使用注解

    @注解名称

    3.4 Java提供了5个基本的注解:

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

    2.@Deprecated:该注解标注的内容,表示已过时

    3.@SuppressWarnings:抑制警告。一般传递参数all,表示抑制所有有告:@SuppressWarnings(“all”)

    4.@SafeVarargs:堆污染警告

    5.@FunctionalInterface:函数式接口

    3.5 自定义注解

    1)格式

    元注解
    public @interface 注解名称{
        属性列表;
    }
    
    • 1
    • 2
    • 3
    • 4

    2)本质

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

    3)属性

    属性:接口中可以定义的成员方法;即称抽象方法就是注解的属性

    对属性有以下要求:

    1.属性的返回值类型有如下选择:
    
        基本数据类型
        String(字符串)
        枚举
        注解
        以上类型的数组
    
    2.定义了属性,在使用时,需要给属性赋值
    
        1.如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值
    
        2.如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可。
    
        3.数组赋值时,值使用{}包裹:
            大括号中有多个值:例如@MyAnno(strs = {"abc", "def"})
            大括号中只有一个值,则大括号{}可以省略,例如@MyAnno(strs =  "abc")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    3.6 元注解

    元注解就是用来描述注解的注解

    1)@Target:描述注解能够作用的位置(作用在类上还是方法上)

    ElementType取值:
    
         TYPE:可以作用于类上,@Target(value = ElementType.TYPE)
         METHOD:可以作用于方法上,@Target(value = ElementType.METHOD)
         FIELD:可以作用于成员变量上,@Target(value = ElementType.FIELD)
    
         若想同时作用于多个位置,可以用大括号{}:@Target(value = {ElementType.TYPE, ElementType.METHOD})
         因为只有一个属性需要赋值,且属性名为value,则可以省略:
             @Target(ElementType.TYPE, ElementType.METHOD)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2)@Target:描述注解能够作用的位置(作用在类上还是方法上)

    RetentionPolicy取值:

    SOURCE:当前被描述的注解,【不会】保留到class字节码文件中,【不会】被JVM读取到
    
    CLASS:当前被描述的注解,【会】保留到class字节码文件中,【不会】被JVM读取到
    
    RUNTIME:当前被描述的注解,【会】保留到class字节码文件中,【并被】JVM读取到
    
    • 1
    • 2
    • 3
    • 4
    • 5

    注意:一般自定义的注解都设置为RUNTIME

    3)@Documented:描述注解是否被抽取到API文档中

    @Documented //表示MyAnno3这个注解会被抽取到javadoc文档中
    public @interface MyAnno3 {
    }
    
    • 1
    • 2
    • 3

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

    @Inherited  //表示子类自动继承该注解
    public @interface MyAnno3 {
    }
    
    • 1
    • 2
    • 3

    3.7 注解使用小结

    1.以后大多数时候,我们会使用注解,而不是自定义注解
    2.注解给谁用?
    1.给编译器用
    2.给解析程序用,例如TestCheck.java
    3.注解不是程序的一部分,可以理解为注解就是一个标签

  • 相关阅读:
    UE导入FBX、GLTF模型
    Mybatis-Plus实现乐观锁
    C++学习笔记(1)--- 常量、数据类型、运算符
    Java InputStream如何读取文件呢?
    Prometheus(三)node-exporter
    21天学Python --- 打卡11:Python基础
    vuepress的使用
    flutter系列之:widgets,构成flutter的基石
    十大排序算法及Java中的排序算法
    LeetCode 热题 1. 两数之和 2. 两数相加 3. 无重复字符的最长子串 4. 寻找两个正序数组的中位数 5. 最长回文子串
  • 原文地址:https://blog.csdn.net/weixin_42214237/article/details/126294276