Java SE 学习笔记(十七)—— 单元测试、反射
目录
- 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 包到模块
- IDEA 通常整合好了
- 编写测试方法:该测试方法必须是公共的无参数无返回值的非静态方法。
- 在测试方法上使用
@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 {// 测试方法(公开的无参数无返回值的非静态方法)@Testpublic void testLogin(){UserService userService = new UserService();String rs = userService.login("admin", "123456");// 进行预期结果的正确性测试Assert.assertEquals("您的登录业务出现问题","登陆成功",rs);}@Testpublic 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;}@Overridepublic 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); // 方法如果是没有返回值的,则返回的是nullSystem.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.classSystem.out.println(list1.getClass()); // class java.util.ArrayListSystem.out.println(list2.getClass()); // class java.util.ArrayListSystem.out.println(list1.getClass() == list2.getClass()); // trueSystem.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); // trueSystem.out.println(list3); // [11, 22, 嘿嘿嘿]// 还有一种方法可以突破泛型的限制ArrayList list4 = list3;list4.add("呜呼啦胡");list4.add(false);System.out.println(list3); // [11, 22, 嘿嘿嘿, 呜呼啦胡, false]}
}
2.6.2 通用框架的底层原理
反射可以做通用框架
需求:给你任意一个对象,在不清楚对象字段的情况可以,可以把对象的字段名称和对应值存储到文件中去。
分析
- 定义一个方法,可以接收任意类的对象。
- 每次收到一个对象后,需要解析这个对象的全部成员变量名称。
- 这个对象可能是任意的,那么怎么样才可以知道这个对象的全部成员变量名称呢?
- 使用反射获取对象的 Class 类对象,然后获取全部成员变量信息。
- 遍历成员变量信息,然后提取本成员变量在对象中的具体值
- 存入成员变量名称和值到文件中去即可
示例代码
存在Teacher、Student类
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);}
}
结果文件

相关文章:
Java SE 学习笔记(十七)—— 单元测试、反射
目录 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 单元测试概…...
HNU-计算机网络-实验1-应用协议与数据包分析实验(Wireshark)
计算机网络 课程基础实验一 应用协议与数据包分析实验(Wireshark) 计科210X 甘晴void 202108010XXX 一、实验目的: 通过本实验,熟练掌握Wireshark的操作和使用,学习对HTTP协议进行分析。 二、实验内容 2.1 HTTP 协议简介 HTTP 是超文本…...
【深度学习】快速制作图像标签数据集以及训练
快速制作图像标签数据集以及训练 制作DataSet 先从网络收集十张图片 每种十张 定义dataSet和dataloader import glob import torch from torch.utils import data from PIL import Image import numpy as np from torchvision import transforms import matplotlib.pyplot…...
Spring Boot Web MVC
文章目录 一、Spring Boot Web MVC 概念二、状态码三、其他注解四、响应操作 一、Spring Boot Web MVC 概念 Spring Web MVC 是⼀个 Web 框架,一开始就包含在Spring 框架里。 1. MVC 定义 软件⼯程中的⼀种软件架构设计模式,它把软件系统分为模型、视…...
设置防火墙
1.RHEL7中的防火墙类型 防火墙只能同时使用一张,firewall底层调用的还是lptables的服务: firewalld:默认 ,基于不同的区域做规则 iptables: RHEL6使用,基于链表 Ip6tables Ebtables 2.防火墙的配置方式 查看防火墙状态: rootlinuxidc -]#systemct…...
3.Docker的客户端指令学习与实战
1.Docker的命令 1.1 启动Docker(systemctl start docker) systemctl start docker1.2 查看docker的版本信息(docker version) docker version1.3 显示docker系统范围的信息(docker info) docker info1.4…...
【微服务开篇-RestTemplate服务调用、Eureka注册中心、Nacos注册中心】
本篇用到的资料:https://gitee.com/Allengan/cloud-demo.githttps://gitee.com/Allengan/cloud-demo.git 目录 1.认识微服务 1.1.单体架构 1.2.分布式架构 1.3.微服务 1.4.SpringCloud 1.5.总结 2.服务拆分和远程调用 2.1.服务拆分原则 2.2.服务拆分示例 …...
python if和while的区别有哪些
python if和while的区别有哪些?下面给大家具体介绍: 1、用法 while和if本身就用法不同,一个是循环语句,一个是判断语句。 2、运行模式 if 只做判断,判断一次之后,便不会再回来了。 while 的话…...
Unity计时器
using UnityEngine; using System.Collections;public class Timer : MonoBehaviour {public float duration 1.0f; // 定时器持续时间public bool isLooping false; // 是否循环public bool isPaused false; // 是否暂停计时器private float currentDuration 0.0f; // 当前…...
Unity热更新介绍
打包函数 BuildPipeline.BuildAssetBundles("AssetBundles", BuildAssetBundleOptions.ChunkBasedCompression, BuildTarget.Android);打包策略和方案 按文件夹打包:Bundle数量少,首次下载块,但是后期更新补丁大按文件打包&#…...
在虚拟机centos7中部署docker+jenkins最新稳定版
在虚拟机centos7中部署dockerjenkins最新稳定版 查看端口是否被占用 lsof -i:80 查看运行中容器 docker ps 查看所有容器 docker ps -a 删除容器 docker rm 镜像/容器名称 强制删除 docker rmi -f 镜像名 查看当前目录 pwd 查看当前目录下所有文件名称 ls 赋予权限 chown 777 …...
nodejs express vue 点餐外卖系统源码
开发环境及工具: nodejs,vscode(webstorm),大于mysql5.5 技术说明: nodejs express vue elementui 功能介绍: 用户端: 登录注册 首页显示搜索菜品,轮播图…...
微信小程序导入js使用时候报错
我是引入weapp库时候,导入js会报错。 需要在小程序开发工具里面配置 就可以了。...
相机存储卡被格式化了怎么恢复?数据恢复办法分享!
随着时代的发展,相机被越来越多的用户所使用,这也意味着更多的用户面临着相机数据丢失的问题,很多用户在使用相机的过程中,都出现过不小心格式化相机存储卡的情况,里面的数据也将一并消失,相机存储卡被格式…...
Firefox修改缓存目录的方法
打开Firefox,在地址栏输入“about:config” 查找是否有 browser.cache.disk.parent_directory,如果没有就新建一个同名的字符串,然后修改值为你要存放Firefox浏览器缓存的目录地址(E:\FirefoxCacheFiles) 然后重新…...
maven子模块无法导入jar包问题
明明本地仓库有jar包 maven子模块无法导入jar包,然后放到父项目的pom.xml则可以导入 可以试试更新仓库后,引入成功...
ardupilot开发 --- 代码解析 篇
0. 前言 根据SITL的断点调试和自己阅读代码的一些理解,写一点自己的注释,有什么不恰当的地方请各位读者不吝赐教。 1. GCS::update_send 线程 主动向MavLink system发送消息包。 1.1 不断向地面站发送飞机状态数据 msg_attitude: msg_location: n…...
C++引用概述
变量名实质上是一段连续存储空间的别名,是一个标号(门牌号),程序中通过变量来申请并命 名内存空间,通过变量的名字可以使用存储空间。引用是 C中新增加的概念,引用可以看作 一个已定义变量的别名。 引用的语法: Type&…...
精准努力,提升自己的核心竞争力——中国人民大学与加拿大女王大学金融硕士
步入职场,相信大家都想成为职场的宠儿。经过一番摸爬滚打后,在职场稳固了地位。但想叱咤职场,还需要精准努力,提升自己的核心竞争力。中国人民大学与加拿大女王大学金融硕士项目为你补给能量。 任何资产都有贬值的风险࿰…...
string【C++】
string 是什么 string 是什么 长度可变的字符串。...
linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...
Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...
Linux --进程控制
本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...
Vue 模板语句的数据来源
🧩 Vue 模板语句的数据来源:全方位解析 Vue 模板(<template> 部分)中的表达式、指令绑定(如 v-bind, v-on)和插值({{ }})都在一个特定的作用域内求值。这个作用域由当前 组件…...
LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)
在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...
鸿蒙(HarmonyOS5)实现跳一跳小游戏
下面我将介绍如何使用鸿蒙的ArkUI框架,实现一个简单的跳一跳小游戏。 1. 项目结构 src/main/ets/ ├── MainAbility │ ├── pages │ │ ├── Index.ets // 主页面 │ │ └── GamePage.ets // 游戏页面 │ └── model │ …...
GraphQL 实战篇:Apollo Client 配置与缓存
GraphQL 实战篇:Apollo Client 配置与缓存 上一篇:GraphQL 入门篇:基础查询语法 依旧和上一篇的笔记一样,主实操,没啥过多的细节讲解,代码具体在: https://github.com/GoldenaArcher/graphql…...
