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

深入解析Java序列化:从使用到原理

在此之前,对于 Java 中的序列化,我一直停留在使用层面 —— 把需要序列化在网络上传输的类实现Serializable接口就可以了

但对于这块知识点,随着工作年限的提升,我觉得必须要好好研究下它了,不能似懂非懂的只知道使用。本文将针对以下几个知识点深入讲解Serializable接口:

  1. 为什么需要序列化实现的类都要添加Serializable接口?
  2. 为什么我自己的项目需要序列化的类没有添加Serializable接口也能成功序列化?
  3. 实现序列化时都建议手动定义一个serialVersionUID,这个属性的作用是什么?

1. Serializable 介绍

Java 序列化是 JDK 1.1 时引入的一组开创性的特性,用于将 Java 对象转换为字节数组,便于存储或传输。此后,仍然可以将字节数组转换回 Java 对象原有的状态

序列化的思想是“冻结”对象状态,然后写到磁盘或者在网络中传输;反序列化 的思想是“解冻”对象状态,重新获得可用的 Java 对象

序列化:Java 对象转换成可在网络中传输或可写入磁盘的一个字节序列

反序列化:将一个字节序列转换为 Java 对象,便于在程序中使用

序列化有一条规则,就是要序列化的对象必须实现Serializbale接口,否则就会报 NotSerializableException 异常

我们先来看看Serializable接口的定义:

public interface Serializable {
}

我们发现,只是定义了接口,但内部没有任何代码。那它是怎么能够保证实现了它的类对象能够被正常的序列化和反序列化呢?

为了能够知道 Java 底层对于序列化、反序列化的实现,我们先定义一个类(Woman)

/*** @Author: ZhangGongMing* @CreateTime: 2025/5/13 13:57* @Description:* @Version: 1.0*/
@Data
public class Woman {private String name;private String age;
}

接着创建一个测试类,通过ObjectOutputStream将“18 岁的王二”写入到文件当中,实际上就是一种序列化的过程;再通过ObjectInputStream将“18 岁的王二”从文件中读出来,实际上就是一种反序列化的过程

/*** @Author: ZhangGongMing* @CreateTime: 2025/5/13 14:12* @Description:* @Version: 1.0*/
public class Main {public static void main(String[] args) {Woman woman = new Woman();woman.setAge("18");woman.setName("张三");System.out.println(woman);// 序列化过程:将对象写入到文件try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("text"))) {oos.writeObject(woman);} catch (IOException e) {e.printStackTrace();}// 反序列化过程:从文件中读出对象try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("text"))) {Woman wm = (Woman) ois.readObject();System.out.println(wm);} catch (IOException | ClassNotFoundException e) {e.printStackTrace();}}
}

我们运行发现,第一个打印能成功执行,但序列化时报错,原因在于我们的Woman类没有实现 Serializable接口。所以在运行的时候会抛出异常。堆栈信息如下:

java.io.NotSerializableException: com.zhang.model.Womanat java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)at com.zhang.Main.main(Main.java:22)

顺着堆栈信息,我们看一下 ObjectOutputStream 的 writeObject0 方法。其部分源码如下:

// 判断对象是否为字符串类型,如果是,则调用 writeString 方法进行序列化
if (obj instanceof String) {writeString((String) obj, unshared);
}
// 判断对象是否为数组类型,如果是,则调用 writeArray 方法进行序列化
else if (cl.isArray()) {writeArray(obj, desc, unshared);
}
// 判断对象是否为枚举类型,如果是,则调用 writeEnum 方法进行序列化
else if (obj instanceof Enum) {writeEnum((Enum<?>) obj, desc, unshared);
}
// 判断对象是否为可序列化类型,如果是,则调用 writeOrdinaryObject 方法进行序列化
else if (obj instanceof Serializable) {writeOrdinaryObject(obj, desc, unshared);
} else {// 如果对象不能被序列化,则抛出 NotSerializableException 异常if (extendedDebugInfo) {throw new NotSerializableException(cl.getName() + "\n" + debugInfoStack.toString());} else {throw new NotSerializableException(cl.getName());}
}

也就是说,ObjectOutputStream 在序列化过程中,会先判断被序列化的对象是哪一种类型,字符串?数组?枚举?还是 Serializable。如果都不是则抛出 NotSerializableException 异常

加入Woman类实现了Serializable接口,就可以成功序列化和反序列化了

/*** @Author: ZhangGongMing* @CreateTime: 2025/5/13 13:57* @Description:* @Version: 1.0*/
@Data
public class Woman implements Serializable {private String name;private String age;
}

其内部是怎么实现的序列化和反序列化呢?

以 ObjectOutputStream 为例,他在序列化时会依次调用:

writeObject()writeObject0()writeOrdinaryObject()writeSerialData()invokeWriteObject()defaultWriteFields()

private void defaultWriteFields(Object obj, ObjectStreamClass desc) throws IOException {// 获取对象的类,并检查是否可以进行默认的序列化Class<?> cl = desc.forClass();desc.checkDefaultSerialize();// 获取对象的基本类型字段的数量,以及这些字段的值int primDataSize = desc.getPrimDataSize();desc.getPrimFieldValues(obj, primVals);// 将基本类型字段的值写入输出流bout.write(primVals, 0, primDataSize, false);// 获取对象的非基本类型字段的值ObjectStreamField[] fields = desc.getFields(false);Object[] objVals = new Object[desc.getNumObjFields()];int numPrimFields = fields.length - objVals.length;desc.getObjFieldValues(obj, objVals);// 循环写入对象的非基本类型字段的值for (int i = 0; i < objVals.length; i++) {// 调用 writeObject0 方法将对象的非基本类型字段序列化写入输出流try {writeObject0(objVals[i], fields[numPrimFields + i].isUnshared());}// 如果在写入过程中出现异常,则将异常包装成 IOException 抛出catch (IOException ex) {if (abortIOException == null) {abortIOException = ex;}}}
}

那怎么反序列化呢?

以 ObjectInputStream 为例,他它在反序列化的时候会依次调用:

readObject()readObject0()readOrdinaryObject()readSerialData()defaultReadFields()

private void defaultReadFields(Object obj, ObjectStreamClass desc) throws IOException {// 获取对象的类,并检查对象是否属于该类Class<?> cl = desc.forClass();if (cl != null && obj != null && !cl.isInstance(obj)) {throw new ClassCastException();}// 获取对象的基本类型字段的数量和值int primDataSize = desc.getPrimDataSize();if (primVals == null || primVals.length < primDataSize) {primVals = new byte[primDataSize];}// 从输入流中读取基本类型字段的值,并存储在 primVals 数组中bin.readFully(primVals, 0, primDataSize, false);if (obj != null) {// 将 primVals 数组中的基本类型字段的值设置到对象的相应字段中desc.setPrimFieldValues(obj, primVals);}// 获取对象的非基本类型字段的数量和值int objHandle = passHandle;ObjectStreamField[] fields = desc.getFields(false);Object[] objVals = new Object[desc.getNumObjFields()];int numPrimFields = fields.length - objVals.length;// 循环读取对象的非基本类型字段的值for (int i = 0; i < objVals.length; i++) {// 调用 readObject0 方法读取对象的非基本类型字段的值ObjectStreamField f = fields[numPrimFields + i];objVals[i] = readObject0(Object.class, f.isUnshared());// 如果该字段是一个引用字段,则将其标记为依赖该对象if (f.getField() != null) {handles.markDependency(objHandle, passHandle);}}if (obj != null) {// 将 objVals 数组中的非基本类型字段的值设置到对象的相应字段中desc.setObjFieldValues(obj, objVals);}passHandle = objHandle;
}

我想看到这里,第一个问题的答案是不是已经有答案了。Serializable接口之所以定义为空,是因为它只是起一个标记作用。告诉 JVM 实现了它的对象是可以被序列化的。而具体的序列化和反序列化操作并不需要它来完成

在使用 Java 自带的序列化方式时,必须要显示的使用 Serializable 接口指定具体需要执行序列化操作的类

但在实际开发中,我们很少使用 JDK 自带的序列化和反序列化。原因如下:

  • 可移植性差:Java 特有的,无法跨语言进行序列化和反序列化
  • 性能差:序列化后的字节体积大,增加了传输/保存成本
  • 安全问题:攻击者可以通过构造恶意数据来实现远程代码执行,从而对系统造成严重的安全威胁。相关阅读:Java 反序列化漏洞之殇

这也解释了为什么我的项目不使用 Serializable 指定类但是能够成功序列化?

因为我没有使用 Java 自身的序列化方式,但是还是建议按照规范在每个需要序列化的类上实现 Serializable 接口。因为你不能保证你们的系统未来不换序列化方式。加入未来换成了 Java 的序列化方式,没有 Serializable 注解会导致程序抛出异常!


2. 注意事项

使用 statictransient 修饰的字段是不会被序列化的

我们在 Woman 类中增加两个字段

/*** @Author: ZhangGongMing* @CreateTime: 2025/5/13 13:57* @Description:* @Version: 1.0*/
@Data
public class Woman implements Serializable {private String name;private String age;// 不会参与序列化private static String address = "银河系";private transient String son = "张三";@Overridepublic String toString() {return "Woman{" + "name=" + name + ",age=" + age + ",address=" + address + ",son=" + son + "}";}
}

修改测试类,在序列化之后反序列以前修改属性,观察 static 修饰的变量

/*** @Author: ZhangGongMing* @CreateTime: 2025/5/13 14:12* @Description:* @Version: 1.0*/
public class Main {public static void main(String[] args) {Woman woman = new Woman();woman.setAge("18");woman.setName("张三");System.out.println(woman);// 序列化过程:将对象写入到文件try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("text"))) {oos.writeObject(woman);} catch (IOException e) {e.printStackTrace();}Woman.address = "中国";// 反序列化过程:从文件中读出对象try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("text"))) {Woman wm = (Woman) ois.readObject();System.out.println(wm);} catch (IOException | ClassNotFoundException e) {e.printStackTrace();}}
}

输出:

Woman{name=张三,age=18,address=银河系,son=张三}
Woman{name=张三,age=18,address=中国,son=null}

从结果的对比中,我们发现:

  1. 序列化前,address 的属性是银河系,反序列后,address 的属性被修改为中国,而不是序列化前的银河系

为什么呢?因为序列化保存的是对象的状态,而static修饰的字段属于类的状态,因此可以证明序列化并不保存static所修饰的字段

  1. 序列化前,son 的值是“张三”。反序列化,son 却为 null。这是为什么?

transient的中文释义为“临时的”。它可以阻止字段被序列化到文件中。在被反序列化后,transient字段的值被设为初始值,比如int型的初始值为 0,对象型的初始值为null

private static ObjectStreamField[] getDefaultSerialFields(Class<?> cl) {// 获取该类中声明的所有字段Field[] clFields = cl.getDeclaredFields();ArrayList<ObjectStreamField> list = new ArrayList<>();int mask = Modifier.STATIC | Modifier.TRANSIENT;// 遍历所有字段,将非 static 和 transient 的字段添加到 list 中for (int i = 0; i < clFields.length; i++) {Field field = clFields[i];int mods = field.getModifiers();if ((mods & mask) == 0) {// 根据字段名、字段类型和字段是否可序列化创建一个 ObjectStreamField 对象ObjectStreamField osf = new ObjectStreamField(field.getName(), field.getType(), !Serializable.class.isAssignableFrom(cl));list.add(osf);}}int size = list.size();// 如果 list 为空,则返回一个空的 ObjectStreamField 数组,否则将 list 转换为 ObjectStreamField 数组并返回return (size == 0) ? NO_FIELDS :list.toArray(new ObjectStreamField[size]);
}

看到Modifier.STATIC | Modifier.TRANSIENT了吧,这两个修饰符标记的字段就没有被放入要序列化的字段列表中


3. serialVersionUID 的作用

我们在实现了 Serializable 接口后,IDEA 往往会提示我们定义一个serialVersionUID。那这个 serialVersionUID到底是什么呢?

serialVersionUID被称为序列化 ID。它是决定 Java 对象能否反序列化成功的重要因子。在反序列化时,JVM 会把字节流中的serialVersionUID与被序列化类中的serialVersionUID进行比较,如果相同则可以进行反序列化,否则就会抛出序列化版本不一致的异常

生成序列化 ID 我们常见的有以下三种方式:

  1. 添加一个默认版本的序列化 ID
private static final long serialVersionUID = 1L。
  1. 添加一个随机生成的不重复的序列化 ID
private static final long serialVersionUID = -2095916884810199532L;
  1. 使用@SuppressWarnings注解
@SuppressWarnings("serial")

比较常见的其实是前两种,第三种可能比较少见:使用@SuppressWarnings("serial")注解时,该注解会为被序列化类自动生成一个随机的序列化 ID

Java 虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,还有一个非常重要的因素就是序列化 ID 是否一致

也就是说,如果没有特殊需求,采用默认的序列化 ID(1L)就可以,这样可以确保代码一致时反序列化成功

这样,开篇提到的第三个问题也得到了很好的解释。总结下其实serialVersionUID只是 JVM 在反序列时进行版本一致性判断的属性

相关文章:

深入解析Java序列化:从使用到原理

在此之前&#xff0c;对于 Java 中的序列化&#xff0c;我一直停留在使用层面 —— 把需要序列化在网络上传输的类实现Serializable接口就可以了 但对于这块知识点&#xff0c;随着工作年限的提升&#xff0c;我觉得必须要好好研究下它了&#xff0c;不能似懂非懂的只知道使用。…...

Python面向对象编程(OOP)深度解析:从封装到继承的多维度实践

引言 面向对象编程(Object-Oriented Programming, OOP)是Python开发中的核心范式&#xff0c;其三大特性——​​封装、继承、多态​​——为构建模块化、可维护的代码提供了坚实基础。本文将通过代码实例与理论结合的方式&#xff0c;系统解析Python OOP的实现机制与高级特性…...

传输层:UDP协议

1.UDP协议特点 2.UDP报文格式 如下&#xff1a; 校验和的计算&#xff1a; 3.例子 UDP&#xff08;User Datagram Protocol&#xff0c;用户数据报协议&#xff09;是一种无连接的传输层协议&#xff0c;其报文格式简单高效&#xff0c;适用于对实时性要求高但允许少量丢包的…...

使用WebStorm打断点调试Vue项目

1&#xff1a;打开WebStorm&#xff0c;先配置vue启动的服务&#xff0c;如下 2&#xff1a;如果上面的选择第4步空白&#xff0c;&#xff0c;就先启动过vue项目&#xff0c;或者在packege.json启动过&#xff0c;就可以直接添加npm服务 3&#xff1a;使用Debug启动项目 4&…...

AI助力:零基础开启编程之旅

一、代码调试 三步解决BUG 1. 错误信息翻译 指令模板&#xff1a; 错误诊断模式我遇到【编程语言】报错“粘贴报错信息“ 请&#xff1a; 用小白能懂的话解释问题本质标注可能引发该错误的三个场景给出最可能的修复方案和其他备选方案 2. 上下文分析 进阶指令 结合上下文代…...

信息学奥赛一本通 1535:【例 1】数列操作

【题目链接】 ybt 1535&#xff1a;【例 1】数列操作 【题目考点】 1. 树状数组 【解题思路】 本题为树状数组模板题&#xff0c;维护区间和&#xff0c;进行单点修改&#xff0c;区间查询。 详细讲解见&#xff1a;洛谷 P3374 【模板】树状数组 1&#xff08;树状数组解法…...

MQTT协议详解:物联网通信的轻量级解决方案

MQTT协议详解&#xff1a;物联网通信的轻量级解决方案 引言 在物联网(IoT)快速发展的今天&#xff0c;设备间高效可靠的通信变得至关重要。MQTT(Message Queuing Telemetry Transport)作为一种轻量级的发布/订阅协议&#xff0c;已成为物联网通信的首选解决方案。本文将深入探…...

【时时三省】(C语言基础)使用字符串处理函数

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 在C函数库中提供了一些用来专门处理字符串的函数&#xff0c;使用方便。几乎所有版本的C语言编译系统都提供这些函数。下面介绍几种常用的函数。 ①puts函数 输出字符串的函数 其一般形式…...

工具学习_VirusTotal使用

VirusTotal Intelligence 允许用户在其庞大的数据集中进行搜索&#xff0c;以查找符合特定条件的文件&#xff0c;例如哈希值、杀毒引擎检测结果、元数据信息、提交时的文件名、文件结构特征、文件大小等。可以说&#xff0c;它几乎是恶意软件领域的“谷歌搜索引擎”。 网页使…...

Linux下mysql的安装与远程链接

linux安装mysql 01下载依赖&#xff1a; 找到网址/download下&#xff1a; 最下面MySQL Community&#xff08;mysql社区版&#xff09; 选择MySQL Community Server 选择对应的mysql版本 操作系统版本选择 根据操作系统的版本选择具体版本号 下载离线版本 安装包详情 0…...

WebpackVite总结篇与进阶

模块化 Webpack Webpack 入口entry 分离app和第三方库入口 这是什么&#xff1f; 这是告诉 webpack 我们想要配置 2 个单独的入口点&#xff08;例如上面的示例&#xff09;。 为什么&#xff1f; 这样你就可以在 vendor.js 中存入未做修改的必要 library 或文件&#xff0…...

数据工具:数据同步工具、数据血缘工具全解析

目录 一、数据同步工具 &#xff08;一&#xff09;数据同步工具的定义与基本原理 &#xff08;二&#xff09;数据同步工具的类型 &#xff08;三&#xff09;数据同步工具的应用场景 二、数据血缘工具 &#xff08;一&#xff09;数据血缘工具的定义与作用 &#xff0…...

贪吃蛇游戏排行榜模块开发总结:从数据到视觉的实现

一、项目背景与成果概览 在完成贪吃蛇游戏核心玩法后,本次开发重点聚焦于排行榜系统的实现。该系统具备以下核心特性: 🌐 双数据源支持:本地存储(localStorage)与远程API自由切换 🕒 时间维度统计:日榜/周榜/月榜/全时段数据筛选 🎮 模式区分:闯关模式(关卡进度…...

亚远景-基于ASPICE的汽车供应链质量管控培训

以下是一份基于ASPICE的汽车供应链质量管控培训的介绍&#xff1a; 培训目标 理解ASPICE核心概念&#xff1a;帮助学员全面掌握ASPICE的框架结构、最新版本的更新内容&#xff0c;深入理解过程评估模型和参考模型的实际应用。 提升开发过程成熟度&#xff1a;通过培训&#x…...

【工作记录】Kong Gateway 入门篇之部署及简单测试

Kong Gateway部署 Kong Gateway 可以通过多种方式部署&#xff0c;包括 Docker、Kubernetes、以及直接安装在操作系统上。以下是常见的部署方法&#xff1a; 使用 Docker 部署 安装 Docker 和 Docker Compose。创建一个 docker-compose.yml 文件&#xff0c;内容如下&#x…...

【Qt】之音视频编程1:QtAV的背景和安装篇

QtAV 背景与核心概念 1. 什么是 QtAV&#xff1f; QtAV 是一个基于 Qt 框架 和 FFmpeg 的多媒体播放库&#xff0c;旨在为 Qt 应用程序提供高性能、跨平台的音视频播放、处理及渲染功能。它封装了 FFmpeg 的底层编解码能力&#xff0c;并通过 Qt 的图形系统&#xff08;如 QM…...

Centos7安装部署wordpress个人博客保姆级教程

目录 关闭防火墙修改yum镜像源安装 php安装mysql安装nginx关闭SELinux配置nginx转发php文件到fpm服务下载wordpress与配置 centos: 7 php:8.1.29 wordpress:6.8.1 nginx:1.26.1 mysql:5.6.51 关闭防火墙 # 停止防火墙 systemctl stop firewalld # 禁止开启启动 systemctl …...

Python-简单网络编程 I

目录 一、UDP 网络程序1. 通信结构图2. Python 代码实现1&#xff09;服务器端2&#xff09;客户端 3. 注意 二、TCP 网络程序1. 通信结构图2. Python 代码实现1&#xff09;服务器端2&#xff09;客户端 3. 注意 三、文件下载1. PyCharm 程序传参1&#xff09;图形化界面传参2…...

深入浅出之STL源码分析8_三个指针

引言 在第一篇文章 深入浅出之STL源码分析1_vector基本操作-CSDN博客 中有引出了下面的几个问题 1.刚才我提到了我的编译器版本是g 11.4.0&#xff0c;而我们要讲解的是STL&#xff08;标准模板库&#xff09;&#xff0c;那么二者之间的关系是什么&#xff1f;STL安装后我们…...

数据结构(七)——图

一、图的定义与基本术语 1.图的定义 图G由顶点集V和边集E组成&#xff0c;记为G(V,E)&#xff0c;其中V(G)表示图G中顶点的有限非空集&#xff1b;E(G)表示图G中顶点之间的关系&#xff08;边&#xff09;的集合 注意&#xff1a;线性表可以是空表&#xff0c;树可以是空树&…...

养生:打造健康生活的全方位策略

在生活节奏不断加快的当下&#xff0c;养生已成为提升生活质量、维护身心平衡的重要方式。从饮食、运动到睡眠&#xff0c;再到心态调节&#xff0c;各个方面的养生之道共同构建起健康生活的坚实基础。以下为您详细介绍养生的关键要点&#xff0c;助您拥抱健康生活。 饮食养生…...

数据结构(2)线性表-顺序表

知道一个算法的好坏怎么去判断以后&#xff0c;就该正式的去学习一些常见的数据结构&#xff0c;当然&#xff0c;这里的数据结构仅仅是初阶&#xff0c;不会挨个一个一个学完&#xff0c;后期慢慢来。 一、数据结构总论 一般按照逻辑结构和存储结构来分类&#xff0c;在初阶…...

【深度学习-Day 10】机器学习基石:从零入门线性回归与逻辑回归

Langchain系列文章目录 01-玩转LangChain&#xff1a;从模型调用到Prompt模板与输出解析的完整指南 02-玩转 LangChain Memory 模块&#xff1a;四种记忆类型详解及应用场景全覆盖 03-全面掌握 LangChain&#xff1a;从核心链条构建到动态任务分配的实战指南 04-玩转 LangChai…...

芯片:金线的作用

一、金线的核心作用&#xff1a;互联芯片与外部封装 金线&#xff08;Gold Wire&#xff09;在芯片制造中主要用于引线键合&#xff08;Wire Bonding&#xff09;&#xff0c;这是将芯片&#xff08;Die&#xff09;与外部封装引脚&#xff08;Lead Frame或Substrate&#xff…...

Vue3+uniapp 封装axios

1.第一步在项目根目录新建utils文件夹&#xff0c;里边新建两个文件request.js和uni-api-promisify.js 2.request.js 代码 要安装axios import axios from axios import { showToast } from /utils/uni-api-promisify// 创建axios实例 const service axios.create({baseURL:…...

Nacos源码—9.Nacos升级gRPC分析七

大纲 10.gRPC客户端初始化分析 11.gRPC客户端的心跳机制(健康检查) 12.gRPC服务端如何处理客户端的建立连接请求 13.gRPC服务端如何映射各种请求与对应的Handler处理类 14.gRPC简单介绍 10.gRPC客户端初始化分析 (1)gRPC客户端代理初始化的源码 (2)gRPC客户端启动的源码…...

与智能体高效协作:Kimi交互逻辑探索与提示词设计实践【附kimi提示词合集下载】

引言&#xff1a;智能时代的人机协作新范式 在持续使用多款AI助手完成技术文档分析、数据分析等任务后&#xff0c;我逐渐意识到工具效能的核心不仅在于技术参数&#xff0c;更在于使用者对交互逻辑的理解深度。本文将基于实际项目经验&#xff0c;探讨智能体交互的本质规律&a…...

Web 架构之负载均衡会话保持

文章目录 一、引言二、思维导图三、负载均衡会话保持的概念3.1 定义3.2 作用 四、负载均衡会话保持的实现方式4.1 基于 IP 地址原理代码示例&#xff08;以 Nginx 为例&#xff09;注释 4.2 基于 Cookie原理代码示例&#xff08;以 HAProxy 为例&#xff09;注释 4.3 基于 SSL …...

遨游卫星电话与普通手机有什么区别?

在数字化浪潮席卷全球的今天&#xff0c;通信设备的角色早已超越传统语音工具&#xff0c;成为连接物理世界与数字世界的核心枢纽。然而&#xff0c;当普通手机在都市丛林中游刃有余时&#xff0c;面对偏远地区、危险作业场景的应急通信需求&#xff0c;其局限性便显露无遗。遨…...

【Redis】谈谈Redis的设计

Redis&#xff08;Remote Dictionary Service&#xff09;是一个高性能的内存键值数据库&#xff0c;其设计核心是速度、简单性和灵活性。以下从架构、数据结构、持久化、网络模型等方面解析 Redis 的设计实现原理&#xff1a; 1. 核心设计思想 内存优先&#xff1a;数据主要存…...

聊天项目总结

目前项目 完成了个人信息修改&#xff0c;添加好友&#xff0c;创建群聊&#xff0c;添加群聊&#xff0c;在线状态&#xff0c;删除好友&#xff0c;退出群&#xff0c;解散群&#xff0c;好友申请&#xff0c;群资料修改&#xff0c;群管理&#xff0c;群主转让&#xff0c;…...

智能手表整机装配作业指导书(SOP)

&#x1f4c4; 智能手表整机装配作业指导书&#xff08;SOP&#xff09; 产品名称&#xff1a;Aurora Watch S1 产品型号&#xff1a;AWS1-BG22 版本号&#xff1a;SOP-AWS1-V1.0 编制日期&#xff1a;2025年5月6日 编制单位&#xff1a;制造工程部&#xff08;ME&#xff09;…...

c语言第一个小游戏:贪吃蛇小游戏05

贪吃蛇脱缰自动向右走&#xff1a;脱缰的野蛇 #include <curses.h> #include <stdlib.h> struct snake{ int hang; int lie; struct snake *next; }; struct snake *head; struct snake *tail; void initNcurse() { initscr(); keypad(stdscr,1); } int …...

ES6中的解构

在 JavaScript&#xff08;包括 TypeScript&#xff09;中&#xff0c;数组解构和对象解构是 ES6 引入的两个非常实用的语法特性&#xff0c;它们可以帮助我们更方便地从数组或对象中提取数据。 一、数组解构&#xff08;Array Destructuring&#xff09; &#x1f4cc; 基本用…...

Pycharm的终端执行allure命令出现command not found

Pycharm的接口自动化项目用的是venv虚拟环境&#xff0c;已下载和配置好Allure路径&#xff0c;查看Allure版本正常。 问题&#xff1a;在重新打开Pycham的项目时&#xff0c;在Pycharm终端中执行allure相关命令就会报错zsh: allure: command not found 原因&#xff1a;在PyC…...

[ctfshow web入门] web72

信息收集 下载index.php并查看&#xff0c;和上题差不多 error_reporting(0); ini_set(display_errors, 0); // 你们在炫技吗&#xff1f; if(isset($_POST[c])){$c $_POST[c];eval($c);$s ob_get_contents();ob_end_clean();echo preg_replace("/[0-9]|[a-z]/i",…...

【Folium】使用离线地图

文章目录 相关文献离线地图下载Folium 使用离线地图 相关文献 Folium — Folium 0.19.5 documentationOffline Map Maker 离线地图下载 我们使用 Offline Map Maker 进行地图下载。 特别注意&#xff1a;Folium 默认支持 WGS84 坐标系&#xff0c;建议下载 WGS84 坐标系的地…...

嵌入式自学第二十天(5.13)

&#xff08;1&#xff09;线性表顺序存储的优缺点&#xff1a; 优点&#xff1a;无需为表中逻辑关系添加额外存储空间&#xff1b; 可以快速随机访问元素&#xff0c;时间复杂度O(1)。 缺点&#xff1a;插入删除需要移动元素O(n&#xff09;&#xff1b; 无法动态存储。 …...

ThingsBoard3.9.1 MQTT Topic(4)

本章中的主题适用于网关设备。 1.网关订阅设备属性的topic&#xff1a;v1/gateway/attributes/response 订阅后接收到的响应格式。 { "id":3, "device":"m1", "values":{ "version":"V1.2"…...

centos中JDK_PATH 如何设置

在 CentOS 7.9 中设置 JDK_PATH&#xff08;即 JAVA_HOME&#xff09;的步骤如下。JAVA_HOME 是一个环境变量&#xff0c;用于指向 Java 开发工具包&#xff08;JDK&#xff09;的安装路径。 1. 查找 JDK 安装路径 首先&#xff0c;你需要找到 JDK 的安装路径。可以通过以下命…...

一次讲清 FP32 / FP16 / BF16 / INT8 / INT4

一次讲清 FP32 / FP16 / BF16 / INT8 / INT4 目标&#xff1a;让你3 分钟读懂格式原理&#xff0c;5 分钟学会选型。 只记一句&#xff1a;“指数定范围&#xff0c;尾数定精度&#xff1b;位宽定显存&#xff0c;硬件定成本”。 1 | 为什么要有这么多格式&#xff1f; …...

PH热榜 | 2025-05-13

1. FirstQuadrant 标语&#xff1a;通过以人为本的人工智能来最大化B2B销售 介绍&#xff1a;销售人工智能&#xff0c;帮助创始人和收益团队提高效率&#xff0c;保持组织有序&#xff0c;并促成更多交易。它通过简化销售幕后工作&#xff0c;确保每个细节都不会遗漏。 产品…...

java基础-泛型

文章目录 目录 文章目录 前言 一、泛型的作用 1.类型安全 2.通用性 这里再举个例子 二、泛型的实现 1.泛型类 2.泛型接口 3.泛型方法 4.T符号的起源&#xff08;额外&#xff09; 三、泛型擦除 四、泛型通配符 1.上界通配符&#xff08; &#xff09; 为什么用于…...

对抗帕金森:在疾病阴影下,如何重掌生活主动权?

帕金森病&#xff0c;一种影响全球超 1000 万人的神经退行性疾病&#xff0c;正无声地改变着患者的生活轨迹。随着大脑中多巴胺分泌减少&#xff0c;患者逐渐出现肢体震颤、肌肉僵硬、步态迟缓等症状&#xff0c;甚至连扣纽扣、端水杯这类日常动作都变得艰难。更棘手的是&#…...

网络协议与系统架构分析实战:工具与方法全解

网络协议与系统架构分析实战&#xff1a;工具与方法全解 在互联网系统的开发、运维与安全分析中&#xff0c;协议解析与抓包分析是不可或缺的核心技能。本文将系统梳理主流协议解析工具、协议自动识别方案&#xff0c;并结合实际抓包案例&#xff0c;讲解如何还原和推测底层系…...

使用PocketFlow构建Web Search Agent

前言 本文介绍的是PocketFlow的cookbook中的pocketflow-agent部分。 回顾一下PocketFlow的核心架构&#xff1a; 每一个节点的架构&#xff1a; 具体介绍可以看上一篇文章&#xff1a; “Pocket Flow&#xff0c;一个仅用 100 行代码实现的 LLM 框架” 实现效果 这个Web S…...

基于STM32、HAL库的TLV320AIC3204IRHBR音频接口芯片驱动程序设计

一、简介: ADAU1701JSTZ-RL 是一款高性能音频编解码器 (Codec),专为便携式和低功耗应用设计。它集成了 ADC、DAC、麦克风前置放大器、耳机放大器和数字信号处理功能,支持 I2S/PCM 音频接口和 I2C 控制接口,非常适合与 STM32 微控制器配合使用。 二、硬件接口: 典型的 ST…...

轻量级高性能推理引擎MNN 学习笔记 02.MNN主要API

1. MNN 主要API 注意&#xff1a;本学习笔记只介绍了我在学习过程中常用的API &#xff0c;更多MNN API 请参考官方文档。 1.1. 推理时操作流程 创建Interpreter &#xff1a; createFromFile()通过Interpreter创建Session &#xff1a;createSession()设置输入数据: getSes…...

STM32 ADC 模数转换器详解:原理、配置与应用

STM32 ADC 模数转换器详解&#xff1a;原理、配置与应用 在嵌入式系统中&#xff0c;模数转换&#xff08;ADC&#xff09;是实现传感器信号采集、信号处理等任务的关键环节。STM32 微控制器作为一款功能强大的 32 位微控制器&#xff0c;其内置的 ADC 模块为开发者提供了高效…...

18.Excel数据透视表:第1部分创建数据透视表

一 什么是数据透视表 通过万花筒可以用不同的方式査看里面画面图像&#xff0c;在excel中可以将数据透视表看作是对准数据的万花筒&#xff0c;用不同角度去观察数据&#xff0c;也可以旋转数据&#xff0c;对数据进行重新排列&#xff0c;对大量的数据可以快速的汇总和建立交叉…...