当前位置: 首页 > 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 是什么 长度可变的字符串。...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动

一、前言说明 在2011版本的gb28181协议中&#xff0c;拉取视频流只要求udp方式&#xff0c;从2016开始要求新增支持tcp被动和tcp主动两种方式&#xff0c;udp理论上会丢包的&#xff0c;所以实际使用过程可能会出现画面花屏的情况&#xff0c;而tcp肯定不丢包&#xff0c;起码…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望

文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例&#xff1a;使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例&#xff1a;使用OpenAI GPT-3进…...

基础测试工具使用经验

背景 vtune&#xff0c;perf, nsight system等基础测试工具&#xff0c;都是用过的&#xff0c;但是没有记录&#xff0c;都逐渐忘了。所以写这篇博客总结记录一下&#xff0c;只要以后发现新的用法&#xff0c;就记得来编辑补充一下 perf 比较基础的用法&#xff1a; 先改这…...

IT供电系统绝缘监测及故障定位解决方案

随着新能源的快速发展&#xff0c;光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域&#xff0c;IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选&#xff0c;但在长期运行中&#xff0c;例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)

推荐 github 项目:GeminiImageApp(图片生成方向&#xff0c;可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...

虚拟电厂发展三大趋势:市场化、技术主导、车网互联

市场化&#xff1a;从政策驱动到多元盈利 政策全面赋能 2025年4月&#xff0c;国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》&#xff0c;首次明确虚拟电厂为“独立市场主体”&#xff0c;提出硬性目标&#xff1a;2027年全国调节能力≥2000万千瓦&#xff0…...

作为测试我们应该关注redis哪些方面

1、功能测试 数据结构操作&#xff1a;验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化&#xff1a;测试aof和aof持久化机制&#xff0c;确保数据在开启后正确恢复。 事务&#xff1a;检查事务的原子性和回滚机制。 发布订阅&#xff1a;确保消息正确传递。 2、性…...

深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏

一、引言 在深度学习中&#xff0c;我们训练出的神经网络往往非常庞大&#xff08;比如像 ResNet、YOLOv8、Vision Transformer&#xff09;&#xff0c;虽然精度很高&#xff0c;但“太重”了&#xff0c;运行起来很慢&#xff0c;占用内存大&#xff0c;不适合部署到手机、摄…...

离线语音识别方案分析

随着人工智能技术的不断发展&#xff0c;语音识别技术也得到了广泛的应用&#xff0c;从智能家居到车载系统&#xff0c;语音识别正在改变我们与设备的交互方式。尤其是离线语音识别&#xff0c;由于其在没有网络连接的情况下仍然能提供稳定、准确的语音处理能力&#xff0c;广…...