Java SE 学习笔记(十七)—— 单元测试、反射

news2024/11/24 7:15:27

目录

  • 1 单元测试
    • 1.1 单元测试概述
    • 1.2 单元测试快速入门
    • 1.3 JUnit 常用注解
  • 2 反射
    • 2.1 反射概述
    • 2.2 获取类对象
    • 2.3 获取构造器对象
    • 2.4 获取成员变量对象
    • 2.5 获取常用方法对象
    • 2.6 反射的作用
      • 2.6.1 绕过编译阶段为集合添加数据
      • 2.6.2 通用框架的底层原理

1 单元测试

1.1 单元测试概述


开发好的系统中存在很多方法,如何对这些方法进行测试?
以前我们都是将代码全部写完再进行测试。其实这样并不是很好。在以后工作的时候,都是写完一部分代码,就测试一部分。这样,代码中的问题可以得到及时修复。也避免了由于代码过多,从而无法准确定位到错误的代码。

单元测试就是针对最小的功能单元编写测试代码, Java 程序最小的功能单元是 方法,因此,单元测试就是针对 Java 方法的测试,进而检查方法的正确性

JUnit 是使用 Java 语言实现的单元测试框架,它是开源的, Java 开发者都应当学习并使用 JUnit 编写单元测试。此外,几乎所有的 IDE 工具都集成了 JUnit,这样我们就可以直接在 IDE 中编写并运行 JUnit测试。

JUnit优点:

  • 可以灵活的选择执行哪些测试方法,也可以一键执行全部测试方法
  • 可以生成全部方法的测试报告,如果测试良好则是绿色;如果测试失败,则变成红色
  • 单元测试中的某个方法测试失败了,不会影响其他测试方法的测试

1.2 单元测试快速入门


需求:使用单元测试进行业务方法预期结果、正确性测试的快速入门

分析:

  • JUnit 的 jar 包导入到项目中
    • IDEA 通常整合好了 JUnit 框架,一般不需要导入。
    • 如果 IDEA 没有整合好,需要自己手工导入如下 2 个 JUnit 的 jar 包到模块
  • 编写测试方法:该测试方法必须是公共的无参数无返回值的非静态方法。
  • 在测试方法上使用 @Test 注解:标注该方法是一个测试方法
  • 在测试方法中完成被测试方法的预期正确性测试。
  • 选中测试方法,选择JUnit 运行 ,如果测试良好则是绿色;如果测试失败,则是红色

示例代码

要测试的方法

public class UserService {
    public String login(String name,String passwd){
        if ("admin".equals(name) && "123456".equals(passwd)){
            return "登陆成功";
        }else{
            return "用户名或密码错误!";
        }
    }
    public void selectNames(){
//        System.out.println(10/0);
        System.out.println("查询全部用户成功!");
    }
}

测试方法

import org.junit.Assert;
import org.junit.Test;

public class TestUserService {
    // 测试方法(公开的无参数无返回值的非静态方法)
    @Test
    public void testLogin(){
        UserService userService = new UserService();
        String rs = userService.login("admin", "123456");
        // 进行预期结果的正确性测试
        Assert.assertEquals("您的登录业务出现问题","登陆成功",rs);
    }
    @Test
    public void testSelectNames(){
        UserService userService = new UserService();
        // 要测试的方法没有返回值,不用断言
        userService.selectNames();
    }
}

一个业务要对应一个测试方法

1.3 JUnit 常用注解


Junit 4.xxxx 版本

在这里插入图片描述

Junit 5.xxxx 版本

在这里插入图片描述

2 反射

2.1 反射概述


反射是指对于任何一个 Class 类,在 " 运行的时候 " 都可以直接得到这个类全部成分。

  • 在运行时 , 可以直接得到这个类的构造器对象: Constructor
  • 在运行时 , 可以直接得到这个类的成员变量对象:Field
  • 在运行时 , 可以直接得到这个类的成员方法对象: Method

这种运行时动态获取类信息以及动态调用类中成分的能力称为 Java 语言的反射机制
反射的第一步都是先得到编译后的 Class 类对象,然后就可以得到 Class 的全部成分。

2.2 获取类对象


获取 class 对象的有以下三种方式

在这里插入图片描述

示例代码

Student

package com.huwei;

public class Student {
    private String name;
    private int age;

    public Student() {
        System.out.println("无参构造器执行!");
    }

    public Student(String name, int age) {
        System.out.println("有参构造器执行!");
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

测试类

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        // 1. 通过Class类中的静态方法forName("全限名")来获取
        Class c = Class.forName("com.huwei.Student");
        System.out.println(c); // class com.huwei.Student

        // 2. 通过class属性来获取
        Class c1 = Student.class;
        System.out.println(c1); // class com.huwei.Student

        // 3. 利用对象的getClass方法来获取
        Student s = new Student();
        Class c2 = s.getClass();
        System.out.println(c2); // class com.huwei.Student
    }
}

注意:

  • 第一种方式forName(String className)中的className为全限名(包名+类名)

已经获取类对象了,接下来就可以获取以下对象

在这里插入图片描述

2.3 获取构造器对象


Class类中用于获取构造器的方法

在这里插入图片描述

示例代码

import java.lang.reflect.Constructor;

public class Test {
    public static void main(String[] args) throws NoSuchMethodException {
        // 获取类对象
        Class c = Student.class;
//        System.out.println(c); // class com.huwei.Student

        // 提取类中全部的构造器(只能是公开的构造器)
        Constructor[] cons1 = c.getConstructors();
        // 遍历构造器
        for (Constructor con : cons1) {
            System.out.println(con.getName() + "=====>" + con.getParameterCount());
        }
        System.out.println("-------------------------------------");
        // 提取类中全部的构造器,包括私有
        Constructor[] cons2 = c.getDeclaredConstructors();
        // 遍历构造器
        for (Constructor con : cons2) {
            System.out.println(con.getName() + "=====>" + con.getParameterCount());
        }
        System.out.println("-------------------------------------");
        // 获取单个构造器(无参构造器),只能是公开的(如果无参构造器私有会报错)
        Constructor con1 = c.getConstructor();
        System.out.println(con1.getName() + "=====>" + con1.getParameterCount());
        System.out.println("-------------------------------------");
        // 获取单个构造器(无参构造器),包括私有
        Constructor con2 = c.getDeclaredConstructor();
        System.out.println(con2.getName() + "=====>" + con2.getParameterCount());
        System.out.println("-------------------------------------");
        // 获取单个构造器(有参构造器),只能是公开的(如果有参构造器私有会报错)
        Constructor con3 = c.getConstructor(String.class, int.class);
        System.out.println(con3.getName() + "=====>" + con3.getParameterCount());
        System.out.println("-------------------------------------");
        // 获取单个构造器(有参构造器),包括私有
        Constructor con4 = c.getDeclaredConstructor(String.class, int.class);
        System.out.println(con4.getName() + "=====>" + con4.getParameterCount());
    }
}

获取构造器的作用依然是初始化一个对象返回

Constructor类中用于创建对象的方法

在这里插入图片描述

import java.lang.reflect.Constructor;

public class Test2 {
    public static void main(String[] args) throws Exception{
        // 获取类对象
        Class c = Student.class;
        
        // 获取单个构造器(无参构造器),包括私有
        Constructor con1 = c.getDeclaredConstructor();
        System.out.println(con1.getName() + "=====>" + con1.getParameterCount());
        // 如果遇到了私有构造器,可以暴力反射
        con1.setAccessible(true); // 权限被打开,仅当前这次
        Student s1 = (Student) con1.newInstance();
        System.out.println(s1);
        
        System.out.println("--------------------------------");

        // 获取单个构造器(有参构造器),包括私有
        Constructor con2 = c.getDeclaredConstructor(String.class, int.class);
        System.out.println(con2.getName() + "=====>" + con2.getParameterCount());
        Student s2 = (Student) con2.newInstance("孙悟空",50000);
        System.out.println(s2);
    }
}

如果遇到非 public 的构造器,需要打开权限(暴力反射),然后再创建对象,说明反射可以破坏封装性,私有的也可以执行了

2.4 获取成员变量对象


Class类中用于获取成员变量的方法

在这里插入图片描述

示例代码

import java.lang.reflect.Field;

public class Test3 {
    public static void main(String[] args) throws NoSuchFieldException {
        // 获取类对象
        Class c = Student.class;
        // 获取全部成员变量,包括私有
        Field[] fields = c.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field.getName()+"=====>"+field.getType());
        }

        System.out.println("----------------------");

        // 获取某一个成员变量,包括私有
//        Field field = c.getDeclaredField("name");
        Field field = c.getDeclaredField("age");
        System.out.println(field.getName()+"=====>"+field.getType());
    }
}

获取成员变量的作用依然是在某个对象中取值、赋值

Filed 类中用于取值、赋值的方法

在这里插入图片描述

示例代码

import java.lang.reflect.Field;

public class Test4 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        // 获取类对象
        Class c = Student.class;
        // 获取某一个成员变量,包括私有
        Field name = c.getDeclaredField("name");

        name.setAccessible(true); // 暴力打开权限
        // 赋值
        Student s = new Student();
        name.set(s,"小明");
        System.out.println(s);
        // 取值
        String name1 = (String) name.get(s);
        System.out.println(name1);
    }
}

2.5 获取常用方法对象


Class类中用于获取成员方法的方法

在这里插入图片描述

获取成员方法的作用依然是在某个对象中执行此方法

Method类中用于触发执行的方法

在这里插入图片描述

示例代码

定义Dog类

public class Dog {
    private String name;

    public Dog() {
    }

    public Dog(String name) {
        this.name = name;
    }

    public void run(){
        System.out.println("狗在跑");
    }
    public String eat(String name){
        System.out.println(name+"在吃");
        return "吃的很开心!";
    }
    private void eat(){
        System.out.println("吃啥");
    }

}

测试类

import java.lang.reflect.Method;

public class Test5 {
    public static void main(String[] args) throws Exception {
        // 获取类对象
        Class c = Dog.class;
        // 提取全部方法,包括私有
        Method[] methods = c.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method.getName() + "=====>" + method.getReturnType() + "====>" + method.getParameterCount());
        }
        // 提取单个方法,包括私有
        Method eat1 = c.getDeclaredMethod("eat"); // 无参的eat方法
        Method eat2 = c.getDeclaredMethod("eat", String.class); // 有参的eat方法

        eat1.setAccessible(true); // 暴力反射

        // 触发方法的执行
        Dog d = new Dog();
        Object rs1 = eat1.invoke(d); // 方法如果是没有返回值的,则返回的是null
        System.out.println(rs1);
        Object rs2 = eat2.invoke(d, "小黑");
        System.out.println(rs2);
    }
}

2.6 反射的作用

2.6.1 绕过编译阶段为集合添加数据


反射是作用在运行时的技术,此时集合的泛型将不能产生约束了,此时是可以为集合存入其他任意类型的元素的。

泛型只是在编译阶段可以约束集合只能操作某种数据类型,在编译成 Class 文件进入运行阶段的时候,其真实类型都是 ArrayList 了,泛型相当于被擦除了。

反射是作用在运行时的技术,此时已经不存在泛型了。

示例代码

import java.lang.reflect.Method;
import java.util.ArrayList;

public class Demo {
    public static void main(String[] args) throws Exception{
        ArrayList<String> list1 = new ArrayList<>();
        ArrayList<Integer> list2 = new ArrayList<>();

        // 编译成 Class 文件进入运行阶段的时候,泛型会自动擦除。===> ArrayList.class
        System.out.println(list1.getClass()); // class java.util.ArrayList
        System.out.println(list2.getClass()); // class java.util.ArrayList
        System.out.println(list1.getClass() == list2.getClass()); // true

        System.out.println("=======================================");

        ArrayList<Integer> list3 = new ArrayList<>();
        list3.add(11);
        list3.add(22);
//        list3.add("哈哈哈"); // 报错
        Class c = list3.getClass(); // ArrayList.class ===> public boolean add(E e)
//        // 定位c中的add方法
        Method add = c.getDeclaredMethod("add", Object.class);
        boolean rs = (boolean)add.invoke(list3, "嘿嘿嘿");
        System.out.println(rs); // true
        System.out.println(list3); // [11, 22, 嘿嘿嘿]

		// 还有一种方法可以突破泛型的限制
		ArrayList list4 = list3;
		list4.add("呜呼啦胡");
		list4.add(false);
		System.out.println(list3); // [11, 22, 嘿嘿嘿, 呜呼啦胡, false]
    }
}

2.6.2 通用框架的底层原理


反射可以做通用框架

需求:给你任意一个对象,在不清楚对象字段的情况可以,可以把对象的字段名称和对应值存储到文件中去。

分析

  • 定义一个方法,可以接收任意类的对象。
  • 每次收到一个对象后,需要解析这个对象的全部成员变量名称。
  • 这个对象可能是任意的,那么怎么样才可以知道这个对象的全部成员变量名称呢?
  • 使用反射获取对象的 Class 类对象,然后获取全部成员变量信息。
  • 遍历成员变量信息,然后提取本成员变量在对象中的具体值
  • 存入成员变量名称和值到文件中去即可

示例代码

存在TeacherStudent

import java.io.FileOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Field;

public class MybatisUtil {
    /**
     保存任意类型的对象
     * @param obj
     */
    public static void save(Object obj){
        try (
                PrintStream ps = new PrintStream(new FileOutputStream("reflect/src/data.txt", true));
        ){
            // 1、提取这个对象的全部成员变量:只有反射可以解决
            Class c = obj.getClass();  //   c.getSimpleName()获取当前类名   c.getName获取全限名:包名+类名
            ps.println("================" + c.getSimpleName() + "================");

            // 2、提取它的全部成员变量
            Field[] fields = c.getDeclaredFields();
            // 3、获取成员变量的信息
            for (Field field : fields) {
                String name = field.getName();
                // 提取本成员变量在obj对象中的值(取值)
                field.setAccessible(true);
                String value = field.get(obj) + "";
                ps.println(name  + "=" + value);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
/**
   目标:提供一个通用框架,支持保存所有对象的具体信息。
 */
public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        Student s = new Student();
        s.setName("猪八戒");
        s.setClassName("西天跑路1班");
        s.setAge(1000);
        s.setHobby("吃,睡");
        s.setSex('男');
        MybatisUtil.save(s);

        Teacher t = new Teacher();
        t.setName("波仔");
        t.setSex('男');
        t.setSalary(6000);
        MybatisUtil.save(t);
    }
}

结果文件

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1147639.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java字节码创建对象指令

接上节。 示例代码&#xff1a; package com.lkl.jvmDemo;public class HelloByteCode {public static void main(String[] args) {HelloByteCode obj new HelloByteCode();} }查看字节码清单javap -c -verbose HelloByteCode Classfile /XXX/com/lkl/jvmDemo/HelloByteCod…

【电路笔记】-交流电阻和阻抗

交流电阻和阻抗 文章目录 交流电阻和阻抗1、概述&#xff1a;电阻率2、交流状态与直流状态近似性3、交流状态与直流状态的差异性3.1 趋肤效应(The Skin Effect)3.2 靠近效应&#xff08;The Proximity Effect&#xff09; 4、总结 电阻是一种特性&#xff0c;用于表征当电压差施…

2023最流行的自动化测试工具有哪些?

一&#xff1a;前言 随着测试工程师技能和工资待遇的提升&#xff0c;甚至有一部分的开发人员开始转入测试岗位&#xff0c;跨入自动化领域的测试攻城狮越来越多。在自动化测试领域&#xff0c;自动化工具肯定占据了核心的位置。 本文总结了常用的测试自动化工具和框架&#x…

【机器学习可解释性】5.SHAP值的高级使用

机器学习可解释性 1.模型洞察的价值2.特征重要性排列3.部分依赖图4.SHAP 值5.SHAP值的高级使用 正文 汇总SHAP值以获得更详细的模型解释 总体回顾 我们从学习排列重要性和部分依赖图开始&#xff0c;以显示学习后的模型的内容。 然后我们学习了SHAP值来分解单个预测的组成部…

汇编语言-div指令溢出问题

汇编语言-div指令溢出问题 8086CPU中被除数保存在ax(16位)或ax和dx&#xff08;32位&#xff09;中&#xff0c;如果被除数为16位&#xff0c;进行除法运算时al保存商&#xff0c;ah保存余数。如果被除数为32位时&#xff0c;进行除法运算时&#xff0c;ax保存商&#xff0c;d…

从最简单基本开始 or 把问题复杂化还自诩为“设计了一个可扩展的系统”?

文章目录 Intro程序员“把问题复杂化”的职业病如何抉择 Intro 刚才看了一段关于在苹果系统中使用numbers表格软件制作记账本的视频教程&#xff1a;当 Excel 交给苹果来设计会变成…&#xff1f;#Numbers 新手教学&#xff0c;以下为最终界面效果&#xff1a; 有些触动&…

网络原理续

传输层的协议也并非就只有UDP和TCP 就拿王者荣耀这个游戏来说 是否需要可靠性是否需要高效率 那是使用TCP还是UDP呢? 当然是都不用, 除了这两个协议外, 有的传输层协议就是为游戏场景量身打造的. 比如说以KCP为代表的一系列协议. 网络层 地址管理路由选择 网络层的代表:…

【FreeRTOS】

FreeRTOS 一、FreeRTOS任务创建和删除1.1 动态方式1.2 静态方式 二、任务挂起和恢复三、中断管理四、临界区保护及调度器的挂起和恢复五、列表项的插入和删除六、时间片调度七、任务状态查询API函数介绍7.1 任务状态查询API7.2 任务运行时间统计API 八、时间管理九、队列十、信…

SpringBoot小项目——简单的小区物业后台管理系统 认证鉴权 用户-角色模型 AOP切面日志 全局异常【源码】

目录 引出一、应用到的技术栈Spring、Spring MVC、Spring Boot基础SpringBoot进阶、SpringMVC原理、AOP切面MyBatis 数据库相关JavaWeb基础&#xff1a;Session等前端Vue、JavaScript、Bootstrap 二、后台管理系统的功能登录功能1.用户名密码登录2.验证码的登录 报修业务的处理…

【多线程面试题十】、说一说notify()、notifyAll()的区别

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;说一说notify()、notify…

C++哈希表:一种快速查找和插入的方法

文章目录 1、前言2、unordered系列关联式容器2.1、 unordered_map2.1.1、unordered_map的文档介绍2.1.2、unordered_map的接口说明 2.2、 unordered_set 3. 底层结构3.1 哈希概念3.2 哈希冲突2.3 哈希函数2.4 哈希冲突解决2.4.1 闭散列2.4.2 开散列 4. 模拟实现4.1 哈希表的改造…

论文速递 TMC 2023 | RoSeFi: 一种利用商用WiFi设备进行稳健的久坐行为监测系统

注1:本文系“最新论文速览”系列之一,致力于简洁清晰地介绍、解读最新的顶会/顶刊论文 TMC 2023 | RoSeFi: 一种利用商用WiFi设备进行稳健的久坐行为监测系统 原文链接:https://ieeexplore.ieee.org/abstract/document/10269067 本文提出了一种稳健的久坐行为监测系统RoSeFi。…

ITSource 分享 第5期【校园信息墙系统】

项目介绍 本期给大家介绍一个 校园信息墙 系统&#xff0c;可以发布信息&#xff0c;表白墙&#xff0c;分享墙&#xff0c;校园二手买卖&#xff0c;咨询分享等墙信息。整个项目还是比较系统的&#xff0c;分为服务端&#xff0c;管理后台&#xff0c;用户Web端&#xff0c;小…

SHCTF 山河CTF Reverse方向[Week1]全WP 详解

文章目录 [WEEK1]ez_asm[WEEK1]easy_re[WEEK1]seed[WEEK1]signin[WEEK1]easy_math[WEEK1]ez_apk [WEEK1]ez_asm 从上往下读&#xff0c;第一处是xor 1Eh&#xff0c;第二处是sub 0Ah&#xff1b;逆向一下先加0A后异或1E 写个EXP data "nhuo[M7mc7uhc$7midgbTf7$7%#ubf7 …

Go命令行参数操作:os.Args、flag包

Go命令行参数操作&#xff1a;os.Args、flag包 最近在写项目时&#xff0c;需要用到命令行传入的参数&#xff0c;正好借此机会整理一下。 1 os.Args&#xff1a;程序运行时&#xff0c;携带的参数&#xff08;包含exe本身&#xff09; package mainimport ("fmt"&q…

ZYNQ连载02-开发环境

ZYNQ连载02-开发环境 1. 官方文档 ZYNQ开发使用的软件为Vivado/Vitis/PetaLinux,软件体积比较大&#xff0c;硬盘保留100G以上的空间&#xff0c;赛灵思提供详细的文档&#xff0c;链接如下&#xff1a; ZYNQ文档 2. Vivido和Vitis安装 赛灵思统一安装程序 3. PetaLinux安装…

OV-VG: A Benchmark for Open-Vocabulary Visual Grounding

OV-VG: A Benchmark for Open-Vocabulary Visual Grounding 一、Abstract 写在前面 又是一周周末&#xff0c;光调代码去了&#xff0c;都没时间看论文了&#xff0c;汗。   这是一篇关于开放词汇定位的文章&#xff0c;也是近两年的新坑&#xff0c;但是资源也是需要不少。 …

波士顿房价预测

目录 1.初始化库并导入数据 2.查看是否有缺失值&#xff0c;查看各个变量的相关性 3.探究各变量之间的相关关系 4.初始化并训练线性模型 5.可视化预测情况 6.模型优化 idea1&#xff1a;减少决策变量 idea2&#xff1a;数据归一化 idea3&#xff1a;尝试其他模型 XGB…

Vue $nextTick 模板解析后在执行的函数

this.$nextTick(()>{ 模板解析后在执行的函数 })

智慧矿山AI算法助力护帮板支护监测,提升安全与效率

在智慧矿山AI算法系列中&#xff0c;护帮板支护监测是保障矿山安全和提高生产效率的重要环节。护帮板作为矿山支护体系中的重要组成部分&#xff0c;在矿山生产中起到了关键的作用。那么&#xff0c;护帮板在哪种状态下是正常打开的呢&#xff1f;本文将对此进行介绍。 护帮板的…