聊聊Java中的动态代理机制

引言

动态代理是Java中一个非常强大的特性,它允许我们在运行时动态地创建代理对象。本文将深入探讨动态代理的工作原理、实现步骤以及在实际项目中的应用。

第一部分:代理模式基础

代理模式是一种结构型设计模式,它为其他对象提供一个代替或占位符以控制对它的访问。这种模式在软件开发中非常常见,因为它提供了一种灵活的方式来间接使用对象。

1.1 代理模式的定义

代理模式涉及一个代表或代理对象,该对象与真实对象具有相同的接口。代理对象在不直接暴露真实对象的情况下,控制对真实对象的访问。

1.2 代理模式的类型

代理模式有几种不同的类型,每种类型都解决特定的问题:

  • 远程代理(Remote Proxy):为位于不同地址空间的对象提供代理,隐藏对象位于远程地址空间的事实。
  • 虚拟代理(Virtual Proxy):延迟创建开销较大的对象,直到真正需要它们。
  • 保护代理(Protection Proxy):控制对原始对象的访问,根据不同的访问权限提供不同的访问策略。
  • 智能引用(Smart Reference):在访问对象时执行额外的动作,如引用计数、线程安全检查等。

1.3 静态代理与动态代理的区别

  • 静态代理:需要程序员手动编写代理类,代理类和真实主题类实现相同的接口,或者代理类与真实主题类继承自同一个抽象类。
  • 动态代理:在运行时动态创建代理类,不需要程序员手动编写代理类。动态代理通过反射API实现,主要涉及到java.lang.reflect包中的Proxy类和InvocationHandler接口。

1.4 静态代理的示例

假设我们有一个Image接口和它的实现类RealImage,我们想要添加一个缓存机制来优化加载过程。

public interface Image {void display();
}public class RealImage implements Image {private String fileName;public RealImage(String fileName) {this.fileName = fileName;}public void display() {System.out.println("Displaying " + fileName);}
}public class ImageProxy implements Image {private RealImage realImage;private boolean isLoaded;public ImageProxy(String fileName) {this.realImage = null;this.isLoaded = false;}public void display() {if (!isLoaded) {realImage = new RealImage("image.jpg");isLoaded = true;}realImage.display();}
}

在这个示例中,ImageProxy类在第一次调用display()方法时才加载RealImage对象,实现了延迟加载。

1.5 静态代理的局限性

尽管静态代理很有用,但它也有局限性。每当我们想要添加新的功能时,我们都需要为每个类编写一个新的代理类。这不仅增加了代码量,而且违反了开闭原则(对扩展开放,对修改封闭)。

1.6 动态代理的优势

动态代理通过在运行时创建代理对象来解决静态代理的局限性。这种方式不需要为每个类编写单独的代理类,大大减少了代码量,并提高了代码的复用性和灵活性。

通过深入理解代理模式的基础,我们可以更好地利用Java中的动态代理机制,来设计出更加灵活和可维护的系统架构。


第二部分:动态代理的工作原理

动态代理是Java语言中一个非常强大的功能,它允许在运行时动态地创建代理类和对象,而不需要显式地编写代理类的代码。这一部分将深入探讨动态代理的工作原理,并通过多个示例来展示其应用。

2.1 反射机制简介

动态代理的实现依赖于Java的反射机制。反射是一种在运行时检查或修改程序行为的能力。通过反射,我们可以在运行时获取类的信息、创建对象、调用方法、访问字段等。

2.2 动态代理类与接口的关系

动态代理类是由Proxy类在运行时动态创建的,它实现了指定的接口。这意味着动态代理对象可以像实现了该接口的任何其他对象一样使用。

2.3 Proxy类和InvocationHandler接口的作用

  • Proxy类:这是Java动态代理的核心类,提供了静态方法newProxyInstance来创建代理类实例。
  • InvocationHandler接口:这是定义代理对象如何处理方法调用的接口。实现这个接口的invoke方法会在代理对象的每个方法调用时被执行。

2.4 动态代理的创建过程

动态代理的创建过程包括以下几个步骤:

  1. 定义接口:定义一个或多个接口,这些接口将由真实对象和代理对象实现。
  2. 实现InvocationHandler:创建一个实现了InvocationHandler接口的类,并在invoke方法中定义代理逻辑。
  3. 使用Proxy.newProxyInstance方法:使用Proxy类的newProxyInstance方法来创建代理对象。

2.5 示例:日志记录动态代理

假设我们有一个Service接口和它的实现RealService,我们想要在调用RealService的方法前后添加日志记录。

public interface Service {void performService();
}public class RealService implements Service {public void performService() {System.out.println("Executing real service.");}
}public class LoggingInvocationHandler implements InvocationHandler {private Object target;public LoggingInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before method: " + method.getName());Object result = method.invoke(target, args);System.out.println("After method: " + method.getName());return result;}
}// 创建代理对象
Service service = (Service) Proxy.newProxyInstance(Service.class.getClassLoader(),new Class<?>[]{Service.class},new LoggingInvocationHandler(new RealService())
);// 使用代理对象
service.performService();

2.6 示例:延迟初始化动态代理

在某些情况下,我们可能希望延迟对象的初始化,直到它真正被需要。

public class VirtualService implements Service {private String data;public VirtualService() {System.out.println("Service is being initialized.");this.data = "Initialized data";}public void performService() {System.out.println("Service is performing with data: " + data);}
}// 使用动态代理实现延迟初始化
Service lazyService = (Service) Proxy.newProxyInstance(Service.class.getClassLoader(),new Class<?>[]{Service.class},new InvocationHandler() {private Service service;@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if (service == null) {System.out.println("Initializing service on demand.");service = new VirtualService();}return method.invoke(service, args);}}
);// 只有在调用performService时,服务才会被初始化
lazyService.performService();

2.7 示例:安全检查动态代理

我们可以创建一个动态代理来控制对某些方法的访问,并进行安全检查。

public class SecureService implements Service {public void performService() {System.out.println("Performing a secure service.");}
}// 安全检查的InvocationHandler
InvocationHandler securityHandler = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if ("performService".equals(method.getName())) {System.out.println("Security check passed.");}return method.invoke(new SecureService(), args);}
};// 创建代理对象
Service secureService = (Service) Proxy.newProxyInstance(Service.class.getClassLoader(),new Class<?>[]{Service.class},securityHandler
);// 使用代理对象
secureService.performService();

通过这些示例,我们可以看到动态代理如何提供一种灵活的方式来增强或修改对象的行为,而无需修改对象本身的代码。动态代理是实现诸如AOP(面向切面编程)等高级技术的基础。

第三部分:动态代理的实现步骤

动态代理是一种强大的技术,它允许在运行时创建代理对象,而无需事先编写代理类的代码。这一部分将详细解释动态代理的实现步骤,并提供多个示例来加深理解。

3.1 定义接口

动态代理的第一步是定义一个或多个接口。这些接口将由真实对象和代理对象实现。接口定义了代理对象可以调用的方法。

示例:定义一个简单的接口
public interface BookService {void displayBooks();
}

3.2 创建InvocationHandler

InvocationHandler是一个接口,它定义了一个invoke方法,该方法将在代理对象的每个方法调用时被执行。你需要创建一个实现了InvocationHandler接口的类,并在invoke方法中定义代理逻辑。

示例:创建一个简单的InvocationHandler
public class BookServiceHandler implements InvocationHandler {private Object target;public BookServiceHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Handling method: " + method.getName());// 可以在这里添加额外的逻辑,如日志记录、权限检查等return method.invoke(target, args);}
}

3.3 使用Proxy.newProxyInstance方法

使用Proxy类的newProxyInstance方法来创建代理对象。这个方法接受三个参数:一个类加载器、一个接口数组和一个InvocationHandler实例。

示例:创建动态代理对象
public class DynamicProxyDemo {public static void main(String[] args) {BookService realService = new RealBookService(); // 真实对象BookServiceHandler handler = new BookServiceHandler(realService);BookService proxyInstance = (BookService) Proxy.newProxyInstance(BookService.class.getClassLoader(),new Class<?>[]{BookService.class},handler);proxyInstance.displayBooks(); // 代理对象调用方法}
}

3.4 示例:添加日志功能

通过动态代理,我们可以轻松地为方法调用添加日志功能。

示例:添加日志功能的InvocationHandler
public class LoggingHandler implements InvocationHandler {private Object target;public LoggingHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Method " + method.getName() + " is called");long startTime = System.currentTimeMillis();Object result = method.invoke(target, args);long endTime = System.currentTimeMillis();System.out.println("Execution time: " + (endTime - startTime) + "ms");return result;}
}

3.5 示例:实现延迟加载

动态代理可以实现延迟加载,即直到真正需要时才创建对象。

示例:延迟加载的InvocationHandler
public class LazyInitializationHandler implements InvocationHandler {private Object target;@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if (target == null) {synchronized (this) {if (target == null) {target = new ExpensiveObject(); // 创建开销较大的对象}}}return method.invoke(target, args);}
}

3.6 示例:实现访问控制

动态代理还可以用于实现访问控制,确保只有授权的用户才能访问特定的方法。

示例:访问控制的InvocationHandler
public class AccessControlHandler implements InvocationHandler {private Object target;private Set<String> authorizedMethods;public AccessControlHandler(Object target, Set<String> authorizedMethods) {this.target = target;this.authorizedMethods = authorizedMethods;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if (authorizedMethods.contains(method.getName())) {return method.invoke(target, args);} else {throw new IllegalAccessException("Access to method " + method.getName() + " is denied.");}}
}

3.7 动态代理的局限性

虽然动态代理提供了极大的灵活性,但它也有一些局限性。例如,动态代理只能为接口创建代理,不能为类创建代理。此外,动态代理的性能开销通常比直接调用方法要高,因此在性能敏感的应用中需要谨慎使用。

通过这些步骤和示例,我们可以看到动态代理如何提供一种灵活的方式来增强或修改对象的行为,而无需修改对象本身的代码。动态代理是实现诸如AOP(面向切面编程)等高级技术的基础。

第四部分:动态代理的应用示例

动态代理是Java编程中一项强大的特性,它允许开发者在运行时动态地生成代理类,从而实现对目标对象的增强。在这一节中,我们将通过几个具体的应用示例来展示动态代理的广泛应用。

4.1 日志记录代理

日志记录是动态代理最常见的应用之一。通过代理,我们可以在不修改原有业务逻辑的情况下,为方法调用添加日志记录功能。

示例:日志记录代理
public class LoggingInvocationHandler implements InvocationHandler {private final Object target;public LoggingInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Method " + method.getName() + " is called");Object result = method.invoke(target, args);System.out.println("Method " + method.getName() + " returned");return result;}
}

4.2 事务管理代理

在企业应用中,事务管理是必不可少的。通过动态代理,我们可以在方法执行前后自动添加事务的开始和提交操作。

示例:事务管理代理
public class TransactionInvocationHandler implements InvocationHandler {private final Object target;public TransactionInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Starting transaction");Object result = method.invoke(target, args);System.out.println("Committing transaction");return result;}
}

4.3 性能监控代理

性能监控是另一个动态代理的用例。通过代理,我们可以监控方法的执行时间,帮助开发者优化性能瓶颈。

示例:性能监控代理
public class PerformanceMonitoringInvocationHandler implements InvocationHandler {private final Object target;public PerformanceMonitoringInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {long startTime = System.currentTimeMillis();Object result = method.invoke(target, args);long endTime = System.currentTimeMillis();System.out.println("Execution time of " + method.getName() + ": " + (endTime - startTime) + "ms");return result;}
}

4.4 安全性检查代理

安全性是应用程序中的一个重要方面。通过动态代理,我们可以在方法调用前后进行安全性检查,确保只有授权的用户才能访问特定的方法。

示例:安全性检查代理
public class SecurityInvocationHandler implements InvocationHandler {private final Object target;private final Map<String, Boolean> permissions;public SecurityInvocationHandler(Object target, Map<String, Boolean> permissions) {this.target = target;this.permissions = permissions;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if (permissions.getOrDefault(method.getName(), false)) {return method.invoke(target, args);} else {throw new SecurityException("Access denied for method: " + method.getName());}}
}

4.5 资源管理代理

资源管理是另一个动态代理的应用场景。例如,我们可以确保在方法执行完毕后,自动关闭数据库连接或文件流。

示例:资源管理代理
public class ResourceManagementInvocationHandler implements InvocationHandler {private final Object target;public ResourceManagementInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try (AutoCloseable resource = acquireResource()) {return method.invoke(target, args);} finally {releaseResource();}}private AutoCloseable acquireResource() {// Acquire resource logicreturn new AutoCloseable() {@Overridepublic void close() {// Close resource logic}};}private void releaseResource() {// Release resource logic}
}

4.6 远程服务代理

动态代理还可以用于远程服务调用,通过代理将方法调用转发到远程服务器。

示例:远程服务代理
public class RemoteServiceInvocationHandler implements InvocationHandler {private final String remoteServiceUrl;public RemoteServiceInvocationHandler(String remoteServiceUrl) {this.remoteServiceUrl = remoteServiceUrl;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// Convert method call to remote service call// Perform remote service call// Return result of remote service call}
}

第五部分:动态代理的优缺点分析

动态代理作为一种设计模式,它在Java中提供了强大的功能来增强对象的行为。然而,像任何技术一样,它也有其优点和缺点。这一部分将详细分析动态代理的优缺点,并提供一些实际的示例来加深理解。

5.1 优点

5.1.1 增加代码的复用性

动态代理允许开发者为一组对象重用相同的代理逻辑,而不需要为每个对象单独编写代码。

示例:复用日志记录逻辑
public class LoggingInvocationHandler implements InvocationHandler {private final Object target;public LoggingInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 日志记录逻辑System.out.println("Entering: " + method.getName());Object result = method.invoke(target, args);System.out.println("Exiting: " + method.getName());return result;}
}

这个LoggingInvocationHandler可以用于任何接口的实现,从而复用了日志记录的代码。

5.1.2 提供接口的动态实现

动态代理可以在运行时动态地为接口提供实现,这使得开发者可以在不修改原有代码的情况下,为接口方法添加新的功能。

示例:动态实现缓存逻辑
public class CachingInvocationHandler implements InvocationHandler {private final Object target;private final Map<String, Object> cache = new HashMap<>();public CachingInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String key = method.getName() + Arrays.deepToString(args);if (cache.containsKey(key)) {return cache.get(key);} else {Object result = method.invoke(target, args);cache.put(key, result);return result;}}
}

这个CachingInvocationHandler可以在运行时为任何接口方法提供缓存功能。

5.1.3 支持AOP编程

动态代理是实现面向切面编程(AOP)的关键技术之一,它允许开发者将横切关注点(如日志记录、事务管理等)与业务逻辑分离。

示例:AOP中的事务管理
public class TransactionalInvocationHandler implements InvocationHandler {private final Object target;public TransactionalInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {// 开启事务Object result = method.invoke(target, args);// 提交事务return result;} catch (Exception e) {// 回滚事务throw e;}}
}

这个TransactionalInvocationHandler可以在AOP框架中用于管理事务。

5.2 缺点

5.2.1 性能开销

动态代理的创建和使用涉及到反射操作,这可能会导致性能开销,尤其是在高频调用的场景下。

示例:性能测试
public class PerformanceTest {public static void main(String[] args) throws InterruptedException {Object target = new RealSubject();InvocationHandler handler = new LoggingInvocationHandler(target);Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),handler);long start = System.nanoTime();for (int i = 0; i < 1000000; i++) {((Subject) proxy).request();}long end = System.nanoTime();System.out.println("Proxy invocation took: " + (end - start) + "ns");}
}

这个性能测试示例显示了动态代理可能带来的性能影响。

5.2.2 限制了代理对象的使用

由于动态代理只能为接口创建代理,这意味着如果目标对象不是一个接口,或者没有实现接口,那么就不能直接使用动态代理。

示例:类而非接口的代理
public class ConcreteClass {public void performAction() {System.out.println("Action performed");}
}

由于ConcreteClass不是接口,也不能实现任何接口,因此不能直接用动态代理来增强它的行为。

5.2.3 增加了系统的复杂性

使用动态代理可能会使系统变得更加复杂,特别是在涉及多个代理和多个横切关注点时。这可能会使得代码难以理解和维护。

示例:复杂的代理链
public class ComplexInvocationHandler implements InvocationHandler {private final Object target;private final List<InvocationHandler> handlers;public ComplexInvocationHandler(Object target, List<InvocationHandler> handlers) {this.target = target;this.handlers = handlers;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {for (InvocationHandler handler : handlers) {method = handler.invoke(proxy, method, args);}return method.invoke(target, args);}
}

在这个示例中,代理链的复杂性可能会随着handlers列表中处理程序的数量和复杂性而增加。

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

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

相关文章

力扣524. 通过删除字母匹配到字典里最长单词

给你一个字符串 s 和一个字符串数组 dictionary &#xff0c;找出并返回 dictionary 中最长的字符串&#xff0c;该字符串可以通过删除 s 中的某些字符得到。 如果答案不止一个&#xff0c;返回长度最长且字母序最小的字符串。如果答案不存在&#xff0c;则返回空字符串。 示…

为什么人工智能用 Python?

为什么人工智能用 Python&#xff1f; 人工智能&#xff08;AI&#xff09;技术取得了飞速发展&#xff0c;从语音识别、图像处理到自然语言处理&#xff0c;而在众多编程语言中&#xff0c;Python 因其简洁易用、丰富的库和社区支持&#xff0c;成为了 AI 开发的首选语言。本…

linux系统——性能检测工具glances

在linux系统中&#xff0c;由python开发的glances工具是一个功能强大的性能检测工具 可以通过yum进行安装 安装glances后&#xff0c;进入命令界面 glance支持网站模式&#xff0c;将监控到的数据以网站形式显示出来 这里需要用python包管理命令 使用glances -w开放…

linux中最基础使用的命令

小白学习记录&#xff1a; 前情提要&#xff1a;Linux命令基础格式!查看 ls看目录的小技巧 进入指定目录 cd查看当前工作目录 pwd创建一个新的目录(文件夹&#xff09; mkdir创建文件 touch查看文件内容 cat、more操作文件、文件夹- 复制 cp- 移动 mv- 删除【危险操作&#xff…

PostgreSQL 远程登录postgres用户不需要密码?免密登录权限设置

PostgreSQL 安装之后&#xff0c;发现登录postgres用户时不需要密码。原因配置远程IP时&#xff0c;IP 地址对应的 method 设置成了 trust。 今天安全测试反馈&#xff0c;pgsql有弱口令问题&#xff0c;于是上去改了pgsql的密码&#xff0c;结果问题还没解决。查看了具体的问…

docker运行centos提示Operation not permitted

1、在docker中运行了centos7镜像 2、进入到centos容器中使用systemctl命令时提示 systemctl Failed to get D-Bus connection: Operation not permitted 3、解决办法 在运行centos镜像的时候加上--privileged参数 4、附上docker官网命令说明截图

ASP.NET 中的 默认应用程序配置源

默认应用程序配置源 从最高级到最低优先级 使用命令行配置提供程序的命令行参数。使用无前缀环境变量配置提供程序的无前缀环境变量,应用程序在环境中运行时的用户机密 Development 。appsettings.{Environment}.json使用JSON 配置提供程序。例如appsettings.Production.json…

OpenHarmony父子组件单项同步使用:@Prop装饰器

Prop装饰的变量可以和父组件建立单向的同步关系。Prop装饰的变量是可变的&#xff0c;但是变化不会同步回其父组件。 说明&#xff1a; 从API version 9开始&#xff0c;该装饰器支持在ArkTS卡片中使用。 概述 Prop装饰的变量和父组件建立单向的同步关系&#xff1a; ● P…

C语言(数据存储)

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸各位能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎~~ &#x1f4a5;个人主页&#xff1a;小羊在奋斗 &#x1f4a5;所属专栏&#xff1a;C语言 本系列文章为个人学习笔记&#xff0c;在这里撰写成文一…

LeetCode 1075,1103,121,42

1075. 项目员工 I 题目链接 1075. 项目员工 I 要求 查询每一个项目中员工的平均工作年限&#xff0c;精确到小数点后两位。 表 P r o j e c t Project Project表中有project_id和employee_id的字段。 E m p l o y e e Employee Employee表中有employee_id、name和experien…

Linux QT和OpenCV冲突

qt.qpa.plugin: Could not load the Qt platform plugin “xcb” in “$HOME/venv/lib/python3.8/site-packages/cv2/qt/plugins” even though it was found. This application failed to start because no Qt platform plugin could be initialized. Reinstalling the applic…

贪心,CF802B. Heidi and Library

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 Problem - 802A - Codeforces 二、解题报告 1、思路分析 这个题相当于你有一个容量为K的Cache&#xff0c;然后给你一系列访存序列 当访问缺失时你不得不替换掉Cache中某些块 学过操作系统都很熟悉页面置…

anaconda怎么运行python

一、在Anaconda Navigator中运行 首先点击菜单栏中的“开始”&#xff0c;在搜索栏中输入“Anaconda”&#xff0c;然后选择“Anaconda Navigator”。 进入主界面&#xff0c;点击“Spyder”中的“Launch”即可。 然后按F5键运行即可。 二、在Anaconda Prompt中运行 也可以在…

Linux - 高级IO

目录 理解五种IO模型非阻塞IO的设置多路转接之select 实现一个简易的select服务器select服务器的优缺点 多路转接之poll 实现一个简易的poll服务器poll服务器的优缺点 多路转接之epoll epoll原理epoll的优势用epoll实现一个简易的echo服务器 epoll的LT和ET工作模式 什么是LT和…

PasteSpider之阿里云OSS功能的设计初衷

前言 在版本v24.6.2.1之后&#xff0c;有一个菜单"OSS配置"&#xff0c;这个配置是干嘛用的呢&#xff1f; 阿里云OSS&#xff0c;或者说云盘&#xff0c;我觉得也可以当CDN使用&#xff0c;比如我们部署了一个网站&#xff0c;为了减少服务器的承载&#xff0c;可以…

数据结构汇总

等同于&#xff1a; 旋转的时候忽略Nil,选装完再加上。

线性数据结构-链表

链表&#xff1a;线性数据结构&#xff08;一维数组结构&#xff09; 线性的数据结构强调存储和顺序 单向链表&#xff08;也称为单链表&#xff09;是一种简单的数据结构&#xff0c;其中每个节点包含两个部分&#xff1a;一个是数据元素&#xff08;或称为值&#xff09;&am…

好用的linux链接工具

工具下载链接&#xff1a; FinalShell SSH工具,服务器管理,远程桌面加速软件,支持Windows,macOS,Linux,版本4.3.10,更新日期2023.12.31 - FinalShell官网FinalShell是一体化的的服务器,网络管理软件,不仅是ssh客户端,还是功能强大的开发,运维工具,充分满足开发,运维需求.特色功…

引领未来,ArmSoM-Sige5震撼发布:RK3576芯片搭载,多媒体应用新宠

在数字化浪潮的推动下&#xff0c;ArmSoM-Sige5携手Rockchip RK3576第二代8纳米高性能AIOT平台&#xff0c;以颠覆性的性能和多功能性&#xff0c;成为多媒体应用的新宠儿。这一全新产品不仅拥有6 TOPS算力NPU和最大可配16GB大内存&#xff0c;更支持4K视频编解码&#xff0c;具…