Java序列化实战:从核心原理到Redis缓存应用与安全避坑指南
1. 从一次线上故障说起为什么序列化不是“存一下”那么简单去年我们系统上线了一个新功能需要把用户复杂的配置对象缓存到Redis里下次启动时直接加载。开发小哥图省事直接让实体类实现了Serializable接口然后用ObjectOutputStream和ObjectInputStream一顿操作把字节数组存进了Redis。上线初期风平浪静直到有一次紧急需求给这个配置实体类加了一个新字段。噩梦开始了新版本服务启动尝试从Redis加载旧版本序列化数据时直接抛出了java.io.InvalidClassException。整个服务启动失败缓存里的历史数据全部“失联”最后只能连夜写脚本清洗缓存数据并紧急回滚版本。这次事故让我彻底明白Java的序列化与反序列化远不是实现个接口、调两个API那么简单。它像是一份你和JVM之间的“契约”一旦签下实现了Serializable就涉及到版本管理、数据安全、性能开销和内存陷阱等一系列深水区问题。网上那些“Java八股文”面试题往往只停留在“什么是序列化”、“如何实现”的表面但真正在生产环境踩过坑的人才知道魔鬼全在细节里。今天我就结合自己趟过的雷把Java序列化与反序列化里里外外扒个清楚从核心机制到隐秘陷阱再到实战中的正确姿势给你一次讲透。2. 序列化的本质对象状态的“脱水”与“复活”我们常说Java是面向对象的语言程序运行时对象生活在JVM堆内存这个“鲜活”的世界里它有状态成员变量的值有行为方法还有一份标识自己身份的“元数据”类信息。序列化本质上就是把这个“鲜活”的对象转换成一种可以独立于JVM进程、便于存储或网络传输的扁平化字节流的过程我习惯称之为“脱水”。反序列化则是逆过程从字节流中重建出内存中的对象即“复活”。2.1 Serializable接口一个“标记”一场“交易”当你让一个类implements Serializable时你其实并没有实现任何方法。这个接口是一个“标记接口”Marker Interface它的唯一作用就是告诉Java虚拟机“我这个类的对象可以被序列化。”但请注意你签下的是一份“默认契约”。一旦实现JVM会使用内置的默认序列化机制。这个机制会通过反射遍历对象图包括对象的所有非transient、非static字段以及这些字段引用的其他对象将它们的值写入字节流。听起来很自动化对吧但这恰恰是许多问题的根源。为什么需要序列化它的应用场景非常广泛持久化存储将对象状态保存到文件或数据库中比如游戏存档、应用配置。网络传输在RPC如Dubbo、gRPC、消息队列如Kafka消息体或Session共享如Tomcat集群时对象需要被编码成字节流在网络上传输。深度拷贝通过序列化再反序列化可以创建一个对象的完整深拷贝避免手动逐层复制的繁琐和错误。缓存如开头案例将对象放入Redis等分布式缓存。2.2 一个最基础的示例感受默认机制我们先来看一个最简单的、也是问题最多的序列化示例。import java.io.*; // 一个实现了Serializable的简单类 class Person implements Serializable { private String name; private int age; // 注意这个字段 private transient String secret; // transient修饰不会被序列化 public Person(String name, int age, String secret) { this.name name; this.age age; this.secret secret; } Override public String toString() { return Person{name name , age age , secret secret }; } } public class BasicSerializationDemo { public static void main(String[] args) { Person person new Person(张三, 25, 我的密码是123); // 序列化到文件 try (ObjectOutputStream oos new ObjectOutputStream(new FileOutputStream(person.dat))) { oos.writeObject(person); System.out.println(序列化完成: person); } catch (IOException e) { e.printStackTrace(); } // 反序列化从文件 try (ObjectInputStream ois new ObjectInputStream(new FileInputStream(person.dat))) { Person deserializedPerson (Person) ois.readObject(); System.out.println(反序列化完成: deserializedPerson); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } } }运行这段代码输出会是序列化完成: Person{name张三, age25, secret我的密码是123} 反序列化完成: Person{name张三, age25, secretnull}关键点解析流程使用ObjectOutputStream和ObjectInputStream配合文件流完成了对象的“存盘”与“读档”。transient关键字secret字段被transient修饰序列化时被直接忽略。反序列化时该字段会被设置为其类型的默认值对象为null数字为0boolean为false。这是保护敏感信息如密码、密钥最简单的方式。默认构造器注意反序列化过程并不调用类的构造器对象是通过直接分配内存并恢复其字段数据来“重建”的。这意味着如果你的对象构造过程有复杂的初始化逻辑这些逻辑在反序列化时不会执行。踩坑心得1transient的误用与局限transient只能阻止默认序列化机制保存该字段。如果你的类自定义了序列化逻辑后面会讲writeObject/readObject你仍然可以手动序列化这个字段。另外它不是访问修饰符不影响字段的可见性public/private。3. 序列化版本号serialVersionUID稳定性的“定海神针”回到开头的故障案例其罪魁祸首就是序列化版本号——serialVersionUID。这是一个长整型常量用于标识序列化类的版本。如果你不显式声明JVM会根据类名、接口名、方法和字段等自动生成一个。问题就出在这个“自动生成”上。自动生成的危害当你修改了类的结构比如增删字段、修改字段类型、修改方法签名JVM重新计算出的serialVersionUID值极有可能发生变化。反序列化时JVM会比较数据流中的serialVersionUID与当前本地类的serialVersionUID如果不一致就会抛出InvalidClassException认为版本不兼容。最佳实践显式声明serialVersionUID为了避免这种因非兼容性变更甚至只是重构了方法顺序导致的意外失败强烈建议为每一个可序列化类显式声明一个serialVersionUID。class Person implements Serializable { // 显式声明版本号 private static final long serialVersionUID 1L; private String name; private int age; // ... 其他代码 }如何管理版本号初始版本从1L开始即可。兼容性变更如果你只是增加了字段反序列化旧数据时新字段会被设为默认值。这通常是可接受的此时不需要改变serialVersionUID。不兼容变更如果你删除了字段、修改了字段类型或名称、改变了类继承结构等旧数据将无法正确映射到新类。此时你应该更新serialVersionUID例如改为2L并在反序列化代码中做好旧版本数据的兼容处理或迁移否则就应让反序列化果断失败避免数据错乱。踩坑心得2版本号是设计的一部分不要把serialVersionUID当成一个魔法数字。它的变更应该和你的数据 schema 变更一起纳入正式的版本管理流程。在微服务架构下如果序列化数据需要在不同版本的服务间传递比如通过消息队列显式声明并谨慎管理版本号是保证系统稳定性的关键。4. 深入自定义序列化夺回控制权默认序列化机制虽然方便但存在诸多问题性能低反射开销、暴露内部结构、无法处理复杂逻辑如加密特定字段。这时我们需要夺回控制权。4.1 使用writeObject和readObject方法在你的可序列化类中可以定义这两个私有方法。JVM在序列化/反序列化时如果检测到它们就会调用它们而不是使用默认机制。class Account implements Serializable { private static final long serialVersionUID 1L; private String username; private transient String password; // 标记为transient不让默认机制处理 public Account(String username, String password) { this.username username; this.password password; } // 自定义序列化逻辑 private void writeObject(ObjectOutputStream oos) throws IOException { oos.defaultWriteObject(); // 先执行默认序列化处理username // 对密码进行简单加密后再序列化 String encryptedPwd simpleEncrypt(password); oos.writeObject(encryptedPwd); } // 自定义反序列化逻辑 private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { ois.defaultReadObject(); // 先执行默认反序列化处理username // 读取加密的密码并解密 String encryptedPwd (String) ois.readObject(); this.password simpleDecrypt(encryptedPwd); } private String simpleEncrypt(String str) { /* 简单加密演示 */ return new StringBuilder(str).reverse().toString(); } private String simpleDecrypt(String str) { /* 简单解密演示 */ return new StringBuilder(str).reverse().toString(); } Override public String toString() { return Account{username username , password password }; } }这样做的好处选择性序列化即使字段不是transient你也可以在writeObject中决定不写它。数据转换可以在写入前加密、压缩在读取后解密、解压。逻辑验证在readObject中可以对恢复的数据进行有效性校验。向后兼容可以手动处理不同版本的数据格式。注意defaultWriteObject()和defaultReadObject()必须作为第一个操作调用它们负责处理非transient和非static的字段。这是一个容易出错的点。4.2 终极控制Externalizable接口如果你觉得Serializable的默认机制干扰太多可以实现Externalizable接口。它继承了Serializable但要求你实现两个方法public interface Externalizable extends Serializable { void writeExternal(ObjectOutput out) throws IOException; void readExternal(ObjectInput in) throws IOException, ClassNotFoundException; }实现Externalizable的类序列化和反序列化过程完全由这两个方法控制JVM不会自动保存/恢复任何字段。同时反序列化时会调用类的公有无参构造器先创建对象然后再调用readExternal方法。class Product implements Externalizable { private static final long serialVersionUID 1L; private String id; private String name; private double price; // 必须提供公有无参构造器 public Product() { System.out.println(调用Product无参构造器); } public Product(String id, String name, double price) { this.id id; this.name name; this.price price; } Override public void writeExternal(ObjectOutput out) throws IOException { // 完全自己控制写什么怎么写 out.writeUTF(id); out.writeUTF(name); out.writeDouble(price); // 可以写额外信息比如版本号 out.writeInt(2); } Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { // 完全自己控制读什么怎么读 this.id in.readUTF(); this.name in.readUTF(); this.price in.readDouble(); // 读取额外信息可用于版本判断 int version in.readInt(); if (version 2) { // 处理新版本数据... } } }Serializable vs Externalizable 如何选Serializable适合大多数场景开发简单默认机制能处理对象图。通过transient和writeObject/readObject也能实现大部分自定义需求。Externalizable当你需要极致的性能省去反射开销或对序列化格式有完全的控制欲时使用。代价是代码量增加且必须维护公有无参构造器和读写逻辑的严格对应。5. 高级议题与隐秘陷阱掌握了基本操作我们来看看那些容易让人栽跟头的高级问题。5.1 序列化对单例模式的破坏单例模式的核心是确保一个类只有一个实例。但序列化可以“绕过”构造器创建新对象。class Singleton implements Serializable { private static final long serialVersionUID 1L; private static final Singleton INSTANCE new Singleton(); private Singleton() {} public static Singleton getInstance() { return INSTANCE; } }如果你序列化这个单例再反序列化你会得到另一个Singleton对象破坏了单例性。解决方案实现readResolve方法在单例类中增加一个readResolve方法反序列化时JVM会调用它并用它的返回值替换反序列化生成的新对象。class Singleton implements Serializable { // ... 其他代码同上 ... // 防止序列化破坏单例 private Object readResolve() throws ObjectStreamException { return INSTANCE; // 直接返回唯一的实例 } }5.2 性能与内存陷阱性能开销Java原生序列化ObjectOutputStream产生的字节流非常臃肿包含了大量类描述信息序列化和反序列化速度也慢。在生产环境的RPC或缓存中强烈不建议使用。应该选择更高效的序列化方案如Protobuf、Kryo、Hessian、JSONJackson/Gson等。内存消耗与OOM风险反序列化过程会创建新对象。如果反序列化一个巨大的对象图比如一个包含百万级元素的HashMap会瞬间消耗大量内存可能引发OutOfMemoryError。对于从不可信源如网络反序列化数据必须考虑数据大小限制。循环引用默认序列化机制可以处理对象间的循环引用A引用BB又引用A但自定义序列化时如果处理不当可能导致栈溢出。5.3 安全漏洞反序列化攻击这是Java序列化一个臭名昭著的问题。攻击者可以构造一个恶意的序列化字节流其中“包裹”了能在反序列化时自动执行的代码利用某些类的readObject方法中的逻辑或第三方库的漏洞如经典的Apache Commons Collections漏洞。一旦你的应用反序列化了这样的数据就可能执行任意命令导致服务器被入侵。如何防御根本方案不要反序列化不可信的数据这是黄金法则。升级与过滤及时升级JDK和第三方库修复已知漏洞。使用安全工具对反序列化操作进行监控和过滤。替换方案在Web应用、RPC等场景彻底放弃Java原生序列化使用JSON、XML等更安全、更透明的数据格式或者使用不依赖反射和动态类加载的序列化框架如Protobuf。使用ObjectInputFilterJDK 9可以设置过滤器限制反序列化时允许的类、数组大小、深度等。// JDK 9 可以使用过滤器 ObjectInputFilter filter ObjectInputFilter.Config.createFilter(maxdepth10;!com.example.恶意类.*); ois.setObjectInputFilter(filter);6. 实战在Spring Boot中优雅地使用序列化在现代Java开发中我们很少直接操作ObjectOutputStream。更多是在框架层面配置序列化方式。以Spring Boot中使用Redis缓存为例看看如何避免开头的坑。默认的JDK序列化不推荐Spring Boot默认使用JdkSerializationRedisSerializer它底层就是Java原生序列化。它会导致Redis中存储的是乱码不可读。存在前面提到的所有问题版本、安全、性能。推荐配置使用JSON序列化通常我们会选择Jackson或Fastjson将对象序列化为JSON字符串存储。Configuration public class RedisConfig { Bean public RedisTemplateString, Object redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplateString, Object template new RedisTemplate(); template.setConnectionFactory(connectionFactory); // 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值 Jackson2JsonRedisSerializerObject serializer new Jackson2JsonRedisSerializer(Object.class); ObjectMapper mapper new ObjectMapper(); // 设置序列化时的行为 mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); // 禁用默认的类型转换避免反序列化时类型不安全 // mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL); // 更安全的做法启用DefaultTyping但只针对非final类并记录类型信息 mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); serializer.setObjectMapper(mapper); // 设置value的序列化规则和key的序列化规则 template.setKeySerializer(new StringRedisSerializer()); // key采用String序列化 template.setValueSerializer(serializer); // value采用Jackson序列化 template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(serializer); template.afterPropertiesSet(); return template; } }关键点StringRedisSerializer确保Redis的key是人类可读的字符串。Jackson2JsonRedisSerializer将对象转为JSON字符串存储可读性好兼容性强。activateDefaultTyping这个配置至关重要。它会在JSON中添加类信息如class: com.example.Person这样反序列化时才能准确地还原成原来的类型。但请注意这同样会引入一定的反序列化安全风险类似Java原生反序列化漏洞因此只应反序列化可信数据源。对于完全可控的内部服务这是常用做法对于开放接口需要更严格的白名单控制。关于Fastjson的坑网络热词里提到了“fastjson 序列化 enum 类型 对象”和“fastjson反序列化漏洞”。Fastjson虽然性能优异但历史上曝出过多个高危反序列化漏洞如利用AutoType特性其默认配置下反序列化行为可能不够安全。在序列化枚举Enum类型时也需要特别注意其默认行为如按名称或序号是否符合预期。在生产环境中如果使用Fastjson务必升级到安全版本并审慎评估和配置AutoType开关及白名单。7. 总结与核心建议Java的序列化机制是一把双刃剑。它内置于语言层面提供了基础的跨进程对象持久化与传输能力但其默认实现的复杂性、性能问题和安全隐患要求开发者必须深入理解其机理。我的核心建议如下明确使用场景仅在确实需要如特定协议的RPC、遗留系统交互时使用Java原生序列化Serializable。对于新的缓存、消息队列、HTTP API优先选择JSON、Protobuf等跨语言、高性能、更安全的方案。显式声明serialVersionUID为每一个Serializable类都写上它这是保证序列化兼容性的第一道保险。善用transient保护敏感字段避免序列化不必要的数据。理解自定义序列化的威力当默认机制不满足需求时用writeObject/readObject或Externalizable来精确控制。警惕安全风险永远不要反序列化来自不可信源的数据。在框架中配置序列化器时如Redis Template理解其安全配置如Jackson的DefaultTyping。关注性能与内存对于大数据量或高频调用原生序列化可能是瓶颈。进行性能测试选择适合的序列化框架。单例防护如果单例类需要序列化务必实现readResolve方法。序列化与反序列化是Java工程师的必修课它连接着内存与世界。理解它不仅能让你在面试中应对“八股文”更能让你在架构设计、代码编写和故障排查时多一份从容少踩一个深坑。希望这篇结合了实战教训的详解能帮你真正掌握这门“对象脱水与复活”的艺术。

相关新闻

AI代码生成安全实践:基于Hooks的实时审计与防护

AI代码生成安全实践:基于Hooks的实时审计与防护

1. 项目概述:当AI开始写代码,安全谁来把关? 最近和团队里的几个资深开发聊天,大家不约而同地提到了同一个现象:现在写代码,越来越离不开各种AI助手了。从GitHub Copilot到各种开源的Coding Agent&#xff0…

2026/8/26 11:37:31 阅读更多 →
STM32CubeMX安装全攻略:从环境准备到工具链配置避坑指南

STM32CubeMX安装全攻略:从环境准备到工具链配置避坑指南

1. 项目概述:为什么STM32CubeMX的安装值得单独拎出来讲?如果你刚开始接触STM32,或者从标准库、HAL库手动配置的时代过来,第一次打开STM32CubeMX时,大概率会被它集成的功能震撼到。这不仅仅是一个引脚配置工具&#xff…

2026/8/26 11:37:31 阅读更多 →
热电偶测温原理与冷端补偿:从选型安装到故障排查全解析

热电偶测温原理与冷端补偿:从选型安装到故障排查全解析

1. 热电偶测温原理与核心概念拆解 说实话,在工业现场摸爬滚打的仪表工程师,谁手里没碰过几根热电偶?这东西看起来就是两根不同材质的金属丝绞在一起,套上保护管,拧进设备里,温度就测出来了。但你真让我讲清…

2026/8/26 11:37:31 阅读更多 →

最新新闻

华中杯数学建模A题解析:从问题理解到模型构建

华中杯数学建模A题解析:从问题理解到模型构建

我无法根据“2024华中杯A-1”这一标题生成符合要求的高质量博文。 原因如下: 该标题不具备可拆解的项目属性 :它是一个竞赛题号(极大概率是“华中地区大学生数学建模竞赛”2024年A题第1问或A组第1题),本身不指向具体…

2026/8/26 12:11:30 阅读更多 →
陶瓷餐具目标检测数据集制作全流程:从采集清洗到YOLOv8训练

陶瓷餐具目标检测数据集制作全流程:从采集清洗到YOLOv8训练

简介:在计算机视觉领域,高质量数据集是训练可靠目标检测模型的基石。实际工程中,数据集的数量、标注质量与格式规范直接决定模型在真实场景中的泛化能力。以陶瓷餐具工业质检为例,公开数据集往往难以覆盖细粒度品类与统一拍摄条件…

2026/8/26 12:11:30 阅读更多 →
从Claude Code动态工作流到Agent Harness架构设计实战

从Claude Code动态工作流到Agent Harness架构设计实战

1. 项目概述:当代码拥有了“工作流”意识最近在折腾AI编程助手时,我发现一个挺有意思的现象:像Claude Code这类工具,它写代码的方式正在发生一种微妙但深刻的转变。它不再仅仅是“我问一句,它答一段”的静态代码补全&a…

2026/8/26 12:11:30 阅读更多 →
Flask框架入门:从零构建Python Web应用的核心技术与实践

Flask框架入门:从零构建Python Web应用的核心技术与实践

1. 为什么是Flask?从“轻量级”说起如果你刚开始接触Python Web开发,或者是从Django那种“大而全”的框架转过来,听到“Flask是一个轻量级Web框架”这句话,可能第一反应是:它功能少、简单、适合小项目。这个理解对&…

2026/8/26 12:10:22 阅读更多 →
C++字符串底层原理与安全打印实践

C++字符串底层原理与安全打印实践

1. 这不是“Hello World”的重复&#xff0c;而是C字符串操作的底层真相很多人学C时&#xff0c;第一行代码是cout << "Hello World";&#xff0c;然后就以为自己掌握了字符串——直到某天在调试一个内存越界崩溃时&#xff0c;发现std::string对象的data()指针…

2026/8/26 12:10:22 阅读更多 →
Python量化交易系统实战:基于LSTM的深度学习股票预测与回测框架

Python量化交易系统实战:基于LSTM的深度学习股票预测与回测框架

简介&#xff1a;量化交易与深度学习结合已成为金融科技领域的热门方向&#xff0c;但如何让模型从研究走向实盘&#xff0c;是许多开发者面临的难题。系统架构设计是量化项目的基石&#xff0c;数据获取、特征工程、模型训练、策略生成与回测验证各环节缺一不可。LSTM凭借对时…

2026/8/26 12:10:22 阅读更多 →

日新闻

Python random 模块常用函数详解:从入门到实战

Python random 模块常用函数详解:从入门到实战

目录 1. 引言2. 准备工作3. 基础随机函数4. 序列相关函数5. 随机种子与复现6. 实战案例7. 注意事项8. 常见问题与排查9. 总结 1. 引言 摘要&#xff1a; 本文系统介绍 Python 标准库 random 模块中最常用的随机数生成函数。内容涵盖基础随机函数&#xff08;random()、unifor…

2026/8/26 0:00:40 阅读更多 →
《Microsoft Sql server 2008 Internals》读书笔记--第三章Databases and Database Files(2)

《Microsoft Sql server 2008 Internals》读书笔记--第三章Databases and Database Files(2)

《Microsoft Sql server 2008 Internals》索引目录&#xff1a; 《Microsoft Sql server 2008 Internals》读书笔记--目录索引 在上篇文章中&#xff0c;主要介绍了创建数据库的基本语法和FileGroup的初步知识。需要注意的是: 关于FileGroup 如果你的系统是用Raid设备直接存…

2026/8/26 1:18:18 阅读更多 →
政务AI智能体怎么建?三种模式、三步路径与四个误区

政务AI智能体怎么建?三种模式、三步路径与四个误区

政务AI智能体已经从概念试点阶段&#xff0c;转入了政务服务的常态化落地应用&#xff1b;在实际使用过程中&#xff0c;它能自主理解办事需求、辅助完成填报申报、开展材料预审&#xff0c;并联动多个系统协同作业&#xff0c;真正嵌入到政务办理的全流程当中。但在落地推进过…

2026/8/26 1:18:18 阅读更多 →

周新闻

[光学原理与应用-521]:对光的错误理解与纠偏

[光学原理与应用-521]:对光的错误理解与纠偏

首先光是一种能量的载体和形态&#xff0c;宏观上观察到的光是由无数个微观的光量子组成的&#xff0c;每个光子在产生的瞬间&#xff0c;其在真空的空间中以确定不变的速度沿着一个初始的方向一直向前&#xff0c;在微观层面&#xff0c;每个光量子的运动轨迹是以波函数所展现…

2026/8/25 3:38:12 阅读更多 →
SIP通话转接原理与REFER方法实战解析

SIP通话转接原理与REFER方法实战解析

1. 通话转接不是“挂断再拨号”&#xff0c;而是SIP会话的动态重定向你有没有遇到过这样的场景&#xff1a;客服坐席A正在和客户通电话&#xff0c;突然需要把这通对话无缝转给专家坐席B&#xff0c;客户完全感知不到中间的断连——既没听到忙音&#xff0c;也没被要求重新拨号…

2026/8/25 3:38:18 阅读更多 →
Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

1. 为什么选择Kolla-ansible来部署单节点OpenStack&#xff1f;如果你正在寻找一种能把OpenStack从“概念”快速变成“可用的实验环境”的方法&#xff0c;那么Kolla-ansible几乎是当前最主流、最省心的选择。我见过太多人卡在手动编译依赖、配置服务、处理版本冲突的泥潭里&am…

2026/8/25 3:38:23 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速&#xff1a;macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/26 3:50:20 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南&#xff1a;3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗&#xff1f;ncmdump解密工具帮你轻松解决这个困…

2026/8/25 10:31:12 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片&#xff1a;为英语学习 App 打造桌面级学习助手适用平台&#xff1a;HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0&#xff08;API 26 Beta&#xff09;新增了 AgentCard 智能体卡片能力&#xff0c;这是继 HMAF&#xff08;鸿蒙智能体框架&#x…

2026/8/26 1:24:05 阅读更多 →