Java设计模式:代理模式的静态和动态之分(八)
在软件设计中,代理模式是一种常用的设计模式,它为我们提供了一种方式来控制对原始对象的访问。在Java中,代理模式尤其重要,因为它不仅增加了代码的灵活性,还提高了系统的可扩展性。本文将深入探讨Java中的代理模式,包括其定义、分类、实现方式以及实际应用场景。
[参见]:
Java设计模式:核心概述(一)
Java设计模式:单例模式之六种实现方式详解(二)
Java设计模式:工厂模式之简单工厂、工厂方法、抽象工厂(三)
Java设计模式:建造者模式之经典与流式的三种实现(四)
Java设计模式:适配器模式的三种形式(五)
Java设计模式:深入装饰器模式的三种写法(六)
Java设计模式:组合模式之透明与安全的两种实现(七)
目录
- 一、代理模式的概念
- 二、代理模式的分类
- 三、代理模式的组成
- 四、代理模式的优缺点
- 4.1 优点
- 4.2 缺点
- 五、代理模式的使用场景
- 1. 日志的采集
- 2. 权限控制
- 3. 实现AOP(面向切面编程)
- 4. MyBatis Mapper
- 5. Spring的事务管理
- 6. 全局捕获异常
- 7. RPC远程调用接口
- 8. 分布式事务原理代理数据源
- 六、代理模式的三种实现
- 6.1 静态代理模式
- 6.2 JDK动态代理的实现
- 6.3 Cglib代理
- 6.4 三种实现的区别和优缺点
- 6.4.1 JDK动态代理
- 6.4.2 cglib动态代理
- 七、注意事项
- 总结
一、代理模式的概念
代理模式(Proxy Pattern)是一种结构型设计模式,它提供了一种将类的功能委托给另一个类的方法。代理类作为原始类的代表,可以在调用原始类的方法之前或之后添加一些额外的逻辑。通过这种方式,代理模式可以控制对原始对象的访问,隐藏其复杂性或增加额外的功能。
二、代理模式的分类
在Java中,代理模式主要分为静态代理和动态代理两种。
- 静态代理:静态代理是在编译时就确定了代理类和被代理类的关系。代理类和被代理类通常会实现相同的接口或继承自相同的父类。静态代理的实现相对简单,但缺点是当需要代理的类增多时,会导致代理类数量剧增,增加了系统的复杂性。
- 动态代理:动态代理是在运行时创建代理类和对象。Java提供了
java.lang.reflect.Proxy
类和java.lang.reflect.InvocationHandler
接口来支持动态代理。动态代理可以灵活地创建代理对象,而无需为每个被代理类编写具体的代理类。这种灵活性使得动态代理在处理大量代理需求时更加高效。
三、代理模式的组成
-
抽象角色(Subject):这是一个接口或抽象类,定义了代理和真实对象需要实现的方法。它充当了客户端与代理/真实对象之间的契约。
-
代理角色(Proxy):代理类实现了抽象角色,并在需要时调用真实对象的方法。代理可以在调用真实对象之前或之后执行一些附加操作,如日志记录、访问控制、性能优化等。
-
真实角色(RealSubject):这是代理所代表的实际对象。它实现了抽象角色,并定义了实际的业务逻辑。客户端通常不直接与真实对象交互,而是通过代理来访问它。
四、代理模式的优缺点
4.1 优点
- 增加额外的操作:代理可以在不修改真实对象的情况下增加额外的操作,如日志记录、安全性检查等。这提供了更好的灵活性和可扩展性。
- 控制访问:代理可以控制对真实对象的访问,例如通过懒加载来延迟创建对象,或者在需要时限制对敏感数据的访问。
- 保护真实对象:代理可以保护真实对象免受不必要的或恶意的访问,从而提高系统的安全性和稳定性。
- 增强功能:代理可以通过装饰器模式的方式增强真实对象的功能,提供额外的服务或修改现有行为。
4.2 缺点
- 性能开销:由于代理增加了额外的层级和调用,可能会导致性能下降。特别是当代理执行复杂操作或网络通信时,这种开销可能更加明显。
- 代码复杂性:实现代理模式可能需要编写额外的代码和类,这可能会增加系统的复杂性和维护成本。需要仔细设计和测试代理类以确保其正确性和可靠性。
- 可能引入错误:如果代理的实现不正确或存在缺陷,可能会导致意外的行为或错误。需要仔细测试和验证代理的逻辑以确保其按预期工作。
五、代理模式的使用场景
Java代理模式在以下场景中的应用:
1. 日志的采集
通过代理模式,我们可以在方法调用前后添加日志记录的逻辑,而无需修改原始类的代码。这有助于跟踪方法的执行情况、性能分析等。
2. 权限控制
在访问敏感资源或执行关键操作时,可以使用代理模式来拦截方法调用并进行权限检查。如果权限不足,则拒绝执行原方法,并返回相应的错误信息。
3. 实现AOP(面向切面编程)
AOP是代理模式的一种高级应用。通过动态代理,我们可以在不修改原始类代码的情况下,为方法调用添加横切关注点,如日志记录、事务管理、安全检查等。Spring AOP就是基于代理模式实现的。
4. MyBatis Mapper
MyBatis使用动态代理生成Mapper接口的实现类。当你调用Mapper接口的方法时,实际上是调用了一个动态生成的代理对象,该对象会根据方法签名和配置信息执行相应的SQL操作。
5. Spring的事务管理
Spring通过AOP和代理模式实现了声明式事务管理。当我们在方法上添加@Transactional
注解时,Spring会为该方法创建一个代理对象,并在代理对象中添加事务管理的逻辑。这样,我们就可以通过调用代理对象来自动管理事务,而无需在代码中显式编写事务管理的代码。
6. 全局捕获异常
通过代理模式,我们可以在方法调用前后添加异常处理的逻辑。当方法抛出异常时,代理对象可以捕获该异常并进行相应的处理,如记录日志、返回统一的错误信息等。这有助于实现全局的异常处理策略。
7. RPC远程调用接口
在RPC框架中,客户端通常不会直接调用远程服务的方法,而是通过调用一个本地代理对象来实现远程调用。这个代理对象负责将方法调用转换为网络请求,并发送给远程服务。远程服务执行完毕后,将结果返回给代理对象,代理对象再将结果返回给客户端。这种方式隐藏了远程调用的复杂性,使得客户端可以像调用本地方法一样调用远程服务。
8. 分布式事务原理代理数据源
在分布式系统中,为了实现跨多个服务或数据库的事务一致性,我们可以使用分布式事务解决方案(如XA事务、TCC事务等)。这些解决方案通常会提供一个代理数据源或代理连接池,用于拦截和管理数据库操作。当应用程序访问数据库时,实际上是访问了这个代理数据源。代理数据源会根据分布式事务的配置和执行情况来决定是否提交或回滚事务操作。这种方式可以在不修改应用程序代码的情况下实现分布式事务的管理和协调。
六、代理模式的三种实现
6.1 静态代理模式
Java中的静态代理模式是一种相对简单的设计模式,它要求代理类和被代理类实现相同的接口或继承自相同的父类。代理类在内部持有被代理类的引用,并在需要时调用被代理类的方法,同时可以在调用前后添加额外的逻辑。
下面是一个简单的静态代理模式的实现:
首先,定义一个接口:
public interface Service {void performTask();
}
然后,创建被代理类,实现该接口:
public class RealService implements Service {@Overridepublic void performTask() {System.out.println("RealService is performing the task.");}
}
接下来,创建代理类,同样实现该接口,并在构造函数中接收一个被代理类的实例:
public class ProxyService implements Service {private final Service realService;public ProxyService(Service realService) {this.realService = realService;}@Overridepublic void performTask() {System.out.println("ProxyService: Before task.");realService.performTask(); // 调用被代理类的方法System.out.println("ProxyService: After task.");}
}
最后,在客户端代码中使用代理类:
public class Client {public static void main(String[] args) {// 创建被代理对象Service realService = new RealService();// 创建代理对象,将被代理对象作为参数传入Service proxyService = new ProxyService(realService);// 通过代理对象执行方法,会触发代理类中的额外逻辑proxyService.performTask();}
}
当运行客户端代码时,输出将会是:
ProxyService: Before task.
RealService is performing the task.
ProxyService: After task.
在这个例子中,ProxyService
是代理类,它增强了RealService
的功能,即在执行任务前后输出了额外的日志信息。客户端代码通过代理类ProxyService
来调用performTask
方法,而不需要直接与被代理类RealService
交互。这种方式允许在不修改原始类的情况下增加新的行为或控制访问。
6.2 JDK动态代理的实现
JDK动态代理是Java提供的一种在运行时创建代理类和对象的方式。它主要利用java.lang.reflect.Proxy
类和java.lang.reflect.InvocationHandler
接口来实现。下面是一个使用JDK动态代理模式的实现:
首先,定义一个接口:
public interface Service {void performTask();
}
然后,创建被代理类,实现该接口:
public class RealService implements Service {@Overridepublic void performTask() {System.out.println("RealService is performing the task.");}
}
接下来,创建一个实现了InvocationHandler
接口的类。这个类的invoke
方法会在代理对象的方法被调用时被执行:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class ServiceInvocationHandler implements InvocationHandler {private final Object target;public ServiceInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before invoking method: " + method.getName());Object result = method.invoke(target, args); // 调用被代理对象的方法System.out.println("After invoking method: " + method.getName());return result;}
}
最后,在客户端代码中使用Proxy.newProxyInstance
方法创建代理对象,并通过该代理对象调用方法:
import java.lang.reflect.Proxy;public class Client {public static void main(String[] args) {// 创建被代理对象Service realService = new RealService();// 创建InvocationHandler实例,将被代理对象传入InvocationHandler handler = new ServiceInvocationHandler(realService);// 使用Proxy.newProxyInstance创建代理对象Service proxyService = (Service) Proxy.newProxyInstance(Service.class.getClassLoader(), // 类加载器new Class<?>[] { Service.class }, // 代理类要实现的接口列表handler // 关联调用处理器);// 通过代理对象执行方法,会触发InvocationHandler中的invoke方法proxyService.performTask();}
}
当运行客户端代码时,输出将会是:
Before invoking method: performTask
RealService is performing the task.
After invoking method: performTask
在这个例子中,ServiceInvocationHandler
是实现了InvocationHandler
接口的调用处理器。当通过代理对象proxyService
调用performTask
方法时,实际上会调用ServiceInvocationHandler
中的invoke
方法。在invoke
方法内部,我们可以添加任何额外的逻辑,比如方法调用前后的日志输出、权限检查等。通过method.invoke(target, args)
语句,我们实际上是在调用被代理对象realService
的performTask
方法。
6.3 Cglib代理
cglib
是一个强大的、高性能的、高质量的代码生成库,它可以在运行时为 Java 类或接口生成子类或代理类。在 Java 代理模式中,当需要代理的类没有实现接口时,可以使用 cglib
来创建一个该类的子类作为代理。
要使用 cglib
,首先需要将其添加到项目的依赖中。如果你使用 Maven,可以在 pom.xml
文件中添加以下依赖:
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version> <!-- 请检查是否有更新的版本 -->
</dependency>
下面是一个使用 cglib
创建代理类的示例:
首先,定义一个普通的类(注意,这个类没有实现任何接口):
public class RealService {public void performTask() {System.out.println("RealService is performing the task.");}
}
然后,创建一个实现了 MethodInterceptor
接口的类,该类将用于拦截对代理类方法的调用:
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class ServiceMethodInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("Before invoking method: " + method.getName());Object result = proxy.invokeSuper(obj, args); // 调用父类(被代理类)的方法System.out.println("After invoking method: " + method.getName());return result;}
}
最后,在客户端代码中使用 Enhancer
类来创建代理对象:
import net.sf.cglib.proxy.Enhancer;public class Client {public static void main(String[] args) {// 创建被代理对象,实际上这里并不需要直接创建,因为cglib会创建它的子类// RealService realService = new RealService();// 创建Enhancer实例,并设置父类为RealServiceEnhancer enhancer = new Enhancer();enhancer.setSuperclass(RealService.class);// 设置回调,即方法拦截器enhancer.setCallback(new ServiceMethodInterceptor());// 创建代理对象RealService proxyService = (RealService) enhancer.create();// 通过代理对象执行方法,会触发ServiceMethodInterceptor中的intercept方法proxyService.performTask();}
}
当运行客户端代码时,输出将会是:
Before invoking method: performTask
RealService is performing the task.
After invoking method: performTask
在这个例子中,ServiceMethodInterceptor
实现了 MethodInterceptor
接口,用于拦截对代理类方法的调用。在 intercept
方法中,我们可以添加任何额外的逻辑,比如方法调用前后的日志输出。通过 proxy.invokeSuper(obj, args)
语句,我们实际上是在调用被代理类 RealService
的 performTask
方法。Enhancer
类用于创建代理类,并通过 create
方法实例化代理对象。
6.4 三种实现的区别和优缺点
代理模式主要有三种实现方式(静态代理、JDK动态代理和cglib动态代理)中由于静态代理通常针对每个具体类编写,不具有通用性,因此这里主要讨论JDK动态代理和cglib动态代理的区别和优缺点。
6.4.1 JDK动态代理
实现方式:
JDK动态代理要求目标类必须实现至少一个接口。代理类是在运行时动态生成的,实现了目标类所实现的所有接口,并通过反射调用目标类的方法。
优点:
- 无需为每个目标类编写具体的代理类,提高了代码的复用性和可维护性。
- 由于代理类实现了接口,因此系统的耦合度较低,更加灵活。
缺点:
- 目标类必须实现至少一个接口,否则无法使用JDK动态代理。
- 在方法调用频繁的情况下,由于使用了反射机制,性能可能较低。
6.4.2 cglib动态代理
实现方式:
cglib动态代理是通过生成目标类的子类来实现的。它不需要目标类实现任何接口,而是通过继承目标类并重写其方法来创建代理类。在方法调用时,通过方法拦截器来增强方法的调用。
优点:
- 目标类无需实现任何接口,更加灵活。
- 在某些场景下,由于直接调用子类方法而无需经过反射,性能可能优于JDK动态代理。
缺点:
- 由于代理类是通过继承目标类来实现的,因此目标类和方法不能声明为final类型。
- 引入了额外的cglib库依赖,增加了项目的复杂性。
- 系统的耦合度可能较高,因为代理类与目标类是继承关系。
JDK动态代理和cglib动态代理各有优缺点,选择哪种方式取决于具体的需求和场景。如果目标类已经实现了接口,那么JDK动态代理是一个不错的选择。如果目标类没有实现接口,或者需要更高的性能,那么可以考虑使用cglib动态代理。不过,在使用cglib时需要注意目标类和方法不能声明为final类型,以及引入额外依赖的问题。
七、注意事项
- 选择合适的代理类型:根据具体需求选择合适的代理类型(如远程代理、虚拟代理等),并确保代理的实现与抽象角色一致。
- 控制代理的复杂性:尽量保持代理类的简洁和清晰,避免引入不必要的复杂性。只在需要时添加额外的功能或操作。
- 注意线程安全问题:如果代理在多线程环境中使用,需要确保它是线程安全的。考虑使用同步机制或线程本地存储来避免竞态条件和数据不一致问题。
- 测试代理的逻辑:仔细测试和验证代理的逻辑以确保其按预期工作。特别关注代理与真实对象之间的交互和边界情况处理。
总结
Java代理模式是一种强大的设计模式,它允许我们通过代理类来控制对原始对象的访问。无论是静态代理还是动态代理,它们都为我们提供了增加额外逻辑、隐藏复杂性以及提高系统可扩展性的能力。在实际开发中,合理地运用代理模式可以使我们的代码更加灵活、可维护。
相关文章:

Java设计模式:代理模式的静态和动态之分(八)
码到三十五 : 个人主页 心中有诗画,指尖舞代码,目光览世界,步履越千山,人间尽值得 ! 在软件设计中,代理模式是一种常用的设计模式,它为我们提供了一种方式来控制对原始对象的访问。在Java中&a…...

【论文通读】AgentStudio: A Toolkit for Building General Virtual Agents
AgentStudio: A Toolkit for Building General Virtual Agents 前言AbstractMotivationFramework评估GUI GroudingReal-World Cross-Application Benchmark Suite Conclusion 前言 来自昆仑万维的一篇智能体环境数据大一统框架工作,对未来计算机智能体的发展具有指…...
wordvect嵌入和bert嵌入的区别
Word2Vec 嵌入和 BERT 嵌入之间有几个关键区别: 训练方式: Word2Vec:Word2Vec 是一个基于神经网络的词嵌入模型,它通过训练一个浅层的神经网络来学习单词的分布式表示。它有两种训练方式:连续词袋模型(CBOW…...

渗透测试练习题解析 5(CTF web)
1、[安洵杯 2019]easy_serialize_php 1 考点:PHP 反序列化逃逸 变量覆盖 【代码审计】 通过 GET 的方式获取参数 f 的值,传递给变量 function 定义一个过滤函数,过滤掉特定字符(用空字符替换) 下面的代码其实没什么用…...

PCA(Principal Component Analysis,主成分分析)
PCA(Principal Component Analysis,主成分分析)是一种在数据分析中广泛应用的统计方法,主要用于数据降维、可视化和去噪。以下是对PCA的发展史、工作原理以及理论基础的详细解释: Principal Component Analysis 一、PC…...

干货 | 探索CUTTag:从样本到文库,实验步步为营!
CUT&Tag(Cleavage Under Targets and Tagmentation)是一种新型DNA-蛋白互作研究技术,主要用于研究转录因子或组蛋白修饰在全基因组上的结合或分布位点。相比于传统的ChIP-seq技术,CUT&Tag反应在细胞内进行,创新…...

提质不增本,降本不降质
#公益巡讲# #质量万里行# 公开课、沙龙活动...

数据结构---顺序表实现
目录 1.顺序表 2.动态顺序表的实现 (4)顺序表初始化 (5)顺序表销毁 (6)顺序表的插入 a.尾插 b.头插 (7)顺序表的删除 a.尾删 b.头删 (8)指定位置之…...
python docx 添加动态表格
在Python中,使用python-docx库可以创建Word文档并添加动态表格。以下是一个简单的例子,演示如何创建一个包含动态内容的表格: from docx import Document# 创建一个Word文档 document Document()# 添加一个标题 document.add_heading(动态表…...

git配置多SSH
目的: 一台电脑可以让github、gitee等账号同时存在,让不同账号配置不同的密钥 第一步:创建不同平台的SSH公钥 执行命令: ssh-keygen -t rsa -C "对应仓库邮箱地址" -f ~/.ssh/id_rsa.github 如果执行上面的命令&…...

IDEA连接SqlServer数据库
目录 下载jar包 下载sqljdbc_12.6压缩包 解压 导入IDEA 新建文件夹 复制粘贴进JDBC文件夹并设为library 编写类及方法 代码 下载jar包 以sqljdbc_12.6为例 下载sqljdbc_12.6压缩包 最新地址:sqljdbc 官方最新地址 解压 解压即用 导入IDEA 新建文件夹 复制…...

LeetCode 378 有序矩阵中第K小的元素
题目信息 LeetoCode地址: . - 力扣(LeetCode) 题解内容大量转载于:. - 力扣(LeetCode) 题目理解 题意很直观,就是求二维矩阵中所有元素排序后第k小的数。 最小堆写法 该写法不再赘述,维护…...

Vue3(domdiff)最长递归子序列求解简易版(超简单)
Vue3(domdiff)最长递归子序列求解简易版 ⚠️ 关键词(每一个都需要理解)js 代码实现写完感想欢迎关注 ⚠️ 关键词(每一个都需要理解) 动态规划(O(N^2))(不提倡…...
LLaMA-Factory+qwen多轮对话微调
LLaMA-Factory地址:https://github.com/hiyouga/LLaMA-Factory/blob/main/README_zh.md qwen地址:https://huggingface.co/Qwen/Qwen-7B-Chat/tree/main 数据准备 数据样例 [ {"id": "x3959", "conversations": [{&qu…...
邦芒面试:如何在面试中巧妙回答自己的缺点
在面试中,被问及自己的缺点时,如何巧妙回答是一门学问。恰当的回答不仅能够展示你的自我认知,还能让面试官看到你的成长潜力和积极态度。 首先,切忌谈一些看似缺点实则优点的话题,如追求完美、待人接物太客气等。这些…...
Android:身份证识别功能实现
说明: 此文使用华为SDK、百度SDK、百度在线API三种方式实现。 一、使用华为SDK实现身份证识别: 说明:免费,不需要联网。 1.AndroidManifest.xml添加权限:<uses-permission android:name"android.permissio…...
MacOS安装Homebrew教程
安装 Homebrew 是在 macOS 上管理软件包的一种简便方法。以下是安装 Homebrew 的步骤: 打开终端:你可以通过在 Spotlight 搜索栏中输入“终端”并按下回车键来打开 macOS 的终端应用程序。 执行安装命令:在终端中粘贴以下命令并按下回车键执…...
laravel如何通过DB获取一条数据并转成数组
在 Laravel 中,你可以使用原生数据库查询构建器(DB facade)来获取一条数据,并将其转换为数组。这可以通过在查询链的末尾调用 first() 方法后,使用 toArray() 方法来实现。first() 方法会返回一个 StdClass 对象&#…...

ENSP USG防火墙接入虚拟机;开启Web访问;
1.添加防火墙及云,启动防火墙; 2.配置桥接网卡; 默认账户:admin 默认密码:Admin123 #第一次登陆需修改密码; 默认G0/0/0口为管理口,而在模拟器中进入防火墙的web需如下配置: 配置 …...

数据结构算法题(力扣)——链表
以下题目建议大家先自己动手练习,再看题解代码。这里只提供一种做法,可能不是最优解。 1. 移除链表元素(OJ链接) 题目描述:给一个链表的头节点 head 和一个整数 val ,删除链表中所有满足值等于 val 的节点…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...

dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...

USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...

AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机
这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机,因为在使用过程中发现 Airsim 对外部监控相机的描述模糊,而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置,最后在源码示例中找到了,所以感…...
日常一水C
多态 言简意赅:就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过,当子类和父类的函数名相同时,会隐藏父类的同名函数转而调用子类的同名函数,如果要调用父类的同名函数,那么就需要对父类进行引用&#…...

AI语音助手的Python实现
引言 语音助手(如小爱同学、Siri)通过语音识别、自然语言处理(NLP)和语音合成技术,为用户提供直观、高效的交互体验。随着人工智能的普及,Python开发者可以利用开源库和AI模型,快速构建自定义语音助手。本文由浅入深,详细介绍如何使用Python开发AI语音助手,涵盖基础功…...