当前位置: 首页 > news >正文

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 包到模块
  • 编写测试方法:该测试方法必须是公共的无参数无返回值的非静态方法。
  • 在测试方法上使用 @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 类对象,然后获取全部成员变量信息。
  • 遍历成员变量信息,然后提取本成员变量在对象中的具体值
  • 存入成员变量名称和值到文件中去即可

示例代码

存在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);}
}

结果文件

在这里插入图片描述

相关文章:

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 一、实验目的&#xff1a; 通过本实验&#xff0c;熟练掌握Wireshark的操作和使用&#xff0c;学习对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 框架&#xff0c;一开始就包含在Spring 框架里。 1. MVC 定义 软件⼯程中的⼀种软件架构设计模式&#xff0c;它把软件系统分为模型、视…...

设置防火墙

1.RHEL7中的防火墙类型 防火墙只能同时使用一张,firewall底层调用的还是lptables的服务: firewalld:默认 &#xff0c;基于不同的区域做规则 iptables: RHEL6使用&#xff0c;基于链表 Ip6tables Ebtables 2.防火墙的配置方式 查看防火墙状态: rootlinuxidc -]#systemct…...

3.Docker的客户端指令学习与实战

1.Docker的命令 1.1 启动Docker&#xff08;systemctl start docker&#xff09; systemctl start docker1.2 查看docker的版本信息&#xff08;docker version&#xff09; docker version1.3 显示docker系统范围的信息&#xff08;docker info&#xff09; docker info1.4…...

【微服务开篇-RestTemplate服务调用、Eureka注册中心、Nacos注册中心】

本篇用到的资料&#xff1a;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的区别有哪些&#xff1f;下面给大家具体介绍&#xff1a; 1、用法 while和if本身就用法不同&#xff0c;一个是循环语句&#xff0c;一个是判断语句。 2、运行模式 if 只做判断&#xff0c;判断一次之后&#xff0c;便不会再回来了。 while 的话&#xf…...

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);打包策略和方案 按文件夹打包&#xff1a;Bundle数量少&#xff0c;首次下载块&#xff0c;但是后期更新补丁大按文件打包&#…...

在虚拟机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 点餐外卖系统源码

开发环境及工具&#xff1a; nodejs&#xff0c;vscode&#xff08;webstorm&#xff09;&#xff0c;大于mysql5.5 技术说明&#xff1a; nodejs express vue elementui 功能介绍&#xff1a; 用户端&#xff1a; 登录注册 首页显示搜索菜品&#xff0c;轮播图&#xf…...

微信小程序导入js使用时候报错

我是引入weapp库时候&#xff0c;导入js会报错。 需要在小程序开发工具里面配置 就可以了。...

相机存储卡被格式化了怎么恢复?数据恢复办法分享!

随着时代的发展&#xff0c;相机被越来越多的用户所使用&#xff0c;这也意味着更多的用户面临着相机数据丢失的问题&#xff0c;很多用户在使用相机的过程中&#xff0c;都出现过不小心格式化相机存储卡的情况&#xff0c;里面的数据也将一并消失&#xff0c;相机存储卡被格式…...

Firefox修改缓存目录的方法

打开Firefox&#xff0c;在地址栏输入“about:config” 查找是否有 browser.cache.disk.parent_directory&#xff0c;如果没有就新建一个同名的字符串&#xff0c;然后修改值为你要存放Firefox浏览器缓存的目录地址&#xff08;E:\FirefoxCacheFiles&#xff09; 然后重新…...

maven子模块无法导入jar包问题

明明本地仓库有jar包 maven子模块无法导入jar包&#xff0c;然后放到父项目的pom.xml则可以导入 可以试试更新仓库后&#xff0c;引入成功...

ardupilot开发 --- 代码解析 篇

0. 前言 根据SITL的断点调试和自己阅读代码的一些理解&#xff0c;写一点自己的注释&#xff0c;有什么不恰当的地方请各位读者不吝赐教。 1. GCS::update_send 线程 主动向MavLink system发送消息包。 1.1 不断向地面站发送飞机状态数据 msg_attitude: msg_location: n…...

C++引用概述

变量名实质上是一段连续存储空间的别名&#xff0c;是一个标号(门牌号)&#xff0c;程序中通过变量来申请并命 名内存空间&#xff0c;通过变量的名字可以使用存储空间。引用是 C中新增加的概念&#xff0c;引用可以看作 一个已定义变量的别名。 引用的语法&#xff1a; Type&…...

精准努力,提升自己的核心竞争力——中国人民大学与加拿大女王大学金融硕士

步入职场&#xff0c;相信大家都想成为职场的宠儿。经过一番摸爬滚打后&#xff0c;在职场稳固了地位。但想叱咤职场&#xff0c;还需要精准努力&#xff0c;提升自己的核心竞争力。中国人民大学与加拿大女王大学金融硕士项目为你补给能量。 任何资产都有贬值的风险&#xff0…...

string【C++】

string 是什么 string 是什么 长度可变的字符串。...

循环冗余码校验CRC码 算法步骤+详细实例计算

通信过程&#xff1a;&#xff08;白话解释&#xff09; 我们将原始待发送的消息称为 M M M&#xff0c;依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)&#xff08;意思就是 G &#xff08; x ) G&#xff08;x) G&#xff08;x) 是已知的&#xff09;&#xff0…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容

基于 ​UniApp + WebSocket​实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配​微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

《基于Apache Flink的流处理》笔记

思维导图 1-3 章 4-7章 8-11 章 参考资料 源码&#xff1a; https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...

根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:

根据万维钢精英日课6的内容&#xff0c;使用AI&#xff08;2025&#xff09;可以参考以下方法&#xff1a; 四个洞见 模型已经比人聪明&#xff1a;以ChatGPT o3为代表的AI非常强大&#xff0c;能运用高级理论解释道理、引用最新学术论文&#xff0c;生成对顶尖科学家都有用的…...

JavaScript基础-API 和 Web API

在学习JavaScript的过程中&#xff0c;理解API&#xff08;应用程序接口&#xff09;和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能&#xff0c;使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配

目录 一、C 内存的基本概念​ 1.1 内存的物理与逻辑结构​ 1.2 C 程序的内存区域划分​ 二、栈内存分配​ 2.1 栈内存的特点​ 2.2 栈内存分配示例​ 三、堆内存分配​ 3.1 new和delete操作符​ 4.2 内存泄漏与悬空指针问题​ 4.3 new和delete的重载​ 四、智能指针…...

LLMs 系列实操科普(1)

写在前面&#xff1a; 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容&#xff0c;原视频时长 ~130 分钟&#xff0c;以实操演示主流的一些 LLMs 的使用&#xff0c;由于涉及到实操&#xff0c;实际上并不适合以文字整理&#xff0c;但还是决定尽量整理一份笔…...

Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案

在大数据时代&#xff0c;海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构&#xff0c;在处理大规模数据抓取任务时展现出强大的能力。然而&#xff0c;随着业务规模的不断扩大和数据抓取需求的日益复杂&#xff0c;传统…...

32单片机——基本定时器

STM32F103有众多的定时器&#xff0c;其中包括2个基本定时器&#xff08;TIM6和TIM7&#xff09;、4个通用定时器&#xff08;TIM2~TIM5&#xff09;、2个高级控制定时器&#xff08;TIM1和TIM8&#xff09;&#xff0c;这些定时器彼此完全独立&#xff0c;不共享任何资源 1、定…...

结构化文件管理实战:实现目录自动创建与归类

手动操作容易因疲劳或疏忽导致命名错误、路径混乱等问题&#xff0c;进而引发后续程序异常。使用工具进行标准化操作&#xff0c;能有效降低出错概率。 需要快速整理大量文件的技术用户而言&#xff0c;这款工具提供了一种轻便高效的解决方案。程序体积仅有 156KB&#xff0c;…...