Java Builder模式实战:告别面条代码,构建优雅不可变对象
1. 从“面条代码”到优雅构建为什么我们需要Builder模式如果你写过Java尤其是处理过那些拥有十几个甚至几十个成员变量的复杂对象你一定对下面这种“面条式”的构造方式不陌生。想象一下你要创建一个User对象它有用户名、密码、邮箱、手机号、昵称、头像、年龄、地址……等等一大堆属性。最直接的写法可能就是搞一个“全能”构造器或者更糟用一堆setter方法在创建后逐个填充。// 反例1超长参数构造器 - 灾难的开始 public User(String username, String password, String email, String phone, String nickname, String avatar, Integer age, String address, String bio, LocalDateTime createTime) { // ... 一堆赋值 } // 使用起来像在读天书 User user new User(张三, 123456, zhangsanexample.com, 13800138000, 三哥, avatar.jpg, 25, 北京市海淀区, 一个程序员, LocalDateTime.now());这段代码的问题显而易见参数顺序必须严格匹配一旦传错编译器都帮不了你可读性极差根本分不清哪个参数对应哪个属性更致命的是如果某些参数是可选的你就得为各种参数组合重载无数个构造器代码立刻变得臃肿不堪。另一种常见的“补救”措施是使用无参构造器加setter// 反例2JavaBean模式 - 构造过程中的不一致状态 User user new User(); user.setUsername(张三); user.setPassword(123456); user.setEmail(zhangsanexample.com); // ... 可能还有一堆setter user.setCreateTime(LocalDateTime.now());这种方式虽然解决了参数过多和可选参数的问题但它带来了一个新的、更隐蔽的缺陷对象在构造过程中可能处于不一致的状态。比如一个User对象在只设置了username和password但还没设置email的时候它算是一个有效的用户吗这种“分步构造”使得对象无法保证在构造完成的那一刻就是完整且有效的。在多线程环境下你甚至可能看到一个构造了一半的、状态混乱的对象被其他线程访问这简直是灾难的源头。Builder模式正是为了解决这些问题而生的。它不是什么高深莫测的“八股文”而是一个实实在在能让你代码更健壮、更易读、更易维护的实用工具。它的核心思想是将复杂对象的构建过程与其表示分离使得同样的构建过程可以创建不同的表示。简单说就是用一个专门的“建造者”对象一步步地、清晰地、安全地把你需要的对象搭起来。接下来我们就深入看看这个“建造者”是如何工作的以及如何把它用得炉火纯青。2. Builder模式的核心机制与经典实现拆解Builder模式属于创建型设计模式它的结构通常包含四个角色产品Product、抽象建造者Builder、具体建造者ConcreteBuilder和指挥者Director。但在Java的实际应用中尤其是处理单个复杂对象时我们常常使用一种更紧凑、更流行的变体——静态内部类Builder。这种实现由Joshua Bloch在《Effective Java》中推广几乎成为了Java界Builder模式的事实标准。2.1 静态内部类Builder的骨架代码让我们直接从一个典型的User类开始看看它是如何被改造的public class User { // 必需参数 (final, 保证不可变) private final String username; private final String password; // 可选参数 private final String email; private final String phone; private final Integer age; // 私有构造器只能通过Builder构建 private User(Builder builder) { this.username builder.username; this.password builder.password; this.email builder.email; this.phone builder.phone; this.age builder.age; // 可以在这里进行统一的校验 if (this.username null || this.password null) { throw new IllegalArgumentException(用户名和密码为必填项); } } // 静态内部类 Builder public static class Builder { // 必需参数 private final String username; private final String password; // 可选参数 - 提供默认值 private String email ; private String phone ; private Integer age 0; // Builder的构造器只接收必需参数 public Builder(String username, String password) { this.username username; this.password password; } // 为每个可选参数提供链式调用的setter方法 public Builder email(String email) { this.email email; return this; // 返回this支持链式调用 } public Builder phone(String phone) { this.phone phone; return this; } public Builder age(Integer age) { this.age age; return this; } // 最终的build()方法返回构建好的产品对象 public User build() { return new User(this); } } // 省略getter方法... }现在创建对象的过程变得清晰而优雅User user new User.Builder(张三, 123456) .email(zhangsanexample.com) .phone(13800138000) .age(25) .build();2.2 这种实现为何如此巧妙深入分析其设计优势第一强制约束与清晰表达。通过Builder的构造器Builder(String username, String password)我们强制要求调用者必须提供用户名和密码这两个核心属性。这就在编译期保证了必需参数的完整性避免了运行时才发现参数缺失的尴尬。同时链式调用email(...).phone(...)让代码读起来就像一句流畅的英文句子清晰地表达了“创建一个拥有这些属性的用户”的意图可读性远超混乱的参数列表。第二对象的不变性Immutability。注意看User类的成员变量都被声明为final。这意味着一旦User对象通过build()方法被创建出来它的状态就再也不能被修改。这是一个极其重要的特性。不可变对象本质上是线程安全的你不需要担心多线程并发修改带来的数据竞争问题它们也更易于推理因为你可以确信一个对象的状态在其生命周期内是恒定不变的。Builder模式是创建复杂不可变对象的绝佳伴侣。第三灵活的构造过程。可选参数可以有默认值如email “”调用者只需设置他们关心的属性。你还可以在build()方法中集中进行有效性校验例如检查邮箱格式、年龄范围确保最终产出的对象是合法、一致的。这种“延迟校验”到最终构建时刻的方式比在每一步setter中校验要灵活和清晰得多。第四与“重叠构造器模式”和“JavaBean模式”的对比。重叠构造器需要为各种参数组合编写大量构造器难以维护JavaBean模式会导致对象状态临时不一致。Builder模式完美规避了这两者的缺点它提供了类似JavaBean的可读性又具备了构造器的安全性和不可变性。注意这里有一个常见的理解误区。有些人认为Builder模式必然会导致代码量增加因为要多写一个内部类。这确实是一个客观存在的开销。但它的收益——代码的可读性、可维护性、安全性的巨大提升——在大多数场景下远超这点微小的成本。对于只有三四个属性的简单对象使用Builder可能是杀鸡用牛刀但对于属性较多尤其是未来可能扩展的对象尽早引入Builder是明智的投资。3. 从入门到精通Builder模式的高级用法与实战技巧掌握了经典实现我们可以看看Builder模式在实际开发中更高级、更灵活的用法。这些技巧能帮你解决更复杂的问题并规避一些常见的坑。3.1 应对继承体系的Builder模式当你的产品类存在继承关系时Builder模式需要一些特殊处理。目标是让子类的Builder能够复用父类Builder的链式方法。这通常通过泛型来实现一种称为“模拟的自我类型simulated self-type”的惯用法。假设我们有一个基础类Animal和它的子类Cat// 基类 Animal public abstract class Animal { protected final String name; protected final int weight; protected Animal(AbstractBuilder? builder) { this.name builder.name; this.weight builder.weight; } // 抽象的Builder基类使用递归泛型定义 public abstract static class AbstractBuilderT extends AbstractBuilderT { protected String name; protected int weight; public T name(String name) { this.name name; return self(); } public T weight(int weight) { this.weight weight; return self(); } protected abstract T self(); // 关键子类实现此方法返回自身类型 public abstract Animal build(); } } // 子类 Cat public class Cat extends Animal { private final String furColor; private Cat(CatBuilder builder) { super(builder); // 调用父类构造器初始化共有属性 this.furColor builder.furColor; } // Cat专属的Builder继承自抽象Builder public static class CatBuilder extends Animal.AbstractBuilderCatBuilder { private String furColor; public CatBuilder furColor(String furColor) { this.furColor furColor; return this; } Override protected CatBuilder self() { return this; // 返回CatBuilder自身 } Override public Cat build() { return new Cat(this); } } }使用起来子类的Builder可以无缝链式调用父类的方法Cat cat new Cat.CatBuilder() .name(咪咪) // 来自父类Animal.Builder的方法 .weight(5) // 来自父类Animal.Builder的方法 .furColor(orange) // CatBuilder自己的方法 .build();这种设计的精妙之处在于泛型T extends AbstractBuilderT它确保了name()、weight()这些方法返回的是子类Builder的具体类型如CatBuilder从而支持在子类Builder上继续调用子类特有的方法如furColor()。这是构建复杂对象继承体系时非常实用的模式。3.2 利用Lombok简化Builder代码及避坑指南手动编写Builder代码尤其是对于属性很多的类确实有些繁琐。这时候Project Lombok就派上用场了。它是一个Java库通过注解自动生成代码如getter setter constructor等其中就包括对Builder模式的支持。使用Lombok上面的User类可以简化为import lombok.Builder; import lombok.Value; Value // 生成一个所有字段都是final的不可变类自动生成getter、equals、hashCode等 Builder // 在编译时自动生成Builder实现 public class User { NonNull // 可以标记非空字段 String username; NonNull String password; String email; String phone; Integer age; }一行Builder注解Lombok就会在编译时为你生成完整的Builder类。使用方式完全一样User user User.builder() .username(张三) .password(123456) .email(zhangsanexample.com) .build();但是使用Lombok Builder有几个必须注意的“坑”与Data或Value的构造器冲突Builder默认会生成一个包含所有字段的全参私有构造器。如果你同时使用了Data生成全参公有构造器或手动定义了构造器可能会产生冲突。通常对于不可变对象使用ValueBuilder是黄金组合。继承支持有限Lombok的Builder对继承的支持不如手动实现的递归泛型方案优雅。在子类上使用Builder默认不会包含父类的属性。你需要使用SuperBuilder注解Lombok 1.18.2来处理继承场景但其配置相对复杂。编译依赖与IDE支持项目所有成员和CI/CD环境都必须配置Lombok插件否则代码在IDE里会报错如提示“找不到builder()方法”。这是一个团队协作成本。默认值问题Lombok生成的Builder其字段的默认值是Java的默认值如null0。如果你想设置业务相关的默认值如email “”需要配合Builder.Default注解使用Builder public class User { private String username; private String password; Builder.Default private String email ; // 设置默认值为空字符串 }提示是否使用Lombok取决于团队约定和项目情况。对于快速原型、内部工具或属性极多的类Lombok能极大提升效率。但对于需要精细控制构建逻辑、有复杂继承关系或作为公共API发布的库手动实现Builder可能更稳妥、更清晰。3.3 在Spring框架及现代Java中的实践在现代Java开发中Builder模式的应用场景非常广泛。Spring Boot配置属性Spring Boot的ConfigurationProperties就大量使用了Builder模式的思想虽然具体实现是通过setter。你可以定义一个属性类Spring Boot会自动从application.yml中绑定值这本质上也是一种外部驱动的构建过程。HTTP客户端/请求构建像OkHttp、Retrofit这样的HTTP客户端库其Request的创建就广泛使用了Builder模式让配置请求头、URL、参数等变得非常清晰。流式APIFluent API设计Builder模式是流式API的基石。Java 8引入的StreamAPI、DateTimeFormatter的创建等都体现了类似的链式调用、逐步构建的思想。替代过时的构造器当你看到一个类的构造器参数超过4个就应该严肃考虑是否要将其重构为Builder模式。这是保持代码长期可维护性的重要习惯。4. 模式辨析、常见陷阱与性能考量任何模式都有其适用边界误用或理解不透彻都会带来问题。我们来辨析几个容易混淆的概念并看看使用Builder时需要注意什么。4.1 Builder模式 vs. 工厂模式这是初学者最容易混淆的一对。它们的核心区别在于意图和复杂度。工厂模式Factory Method/Abstract Factory关注的是对象的创建过程本身重点是“如何创建”。它用于封装创建逻辑尤其是在需要根据条件创建不同系列或等级的产品时。比如一个“图形渲染器工厂”根据平台Windows/Linux创建不同的渲染器实例。调用者不关心具体创建细节。Builder模式关注的是复杂对象的组装过程重点是“如何配置”。它用于分步构建一个具有许多部件属性的复杂对象。调用者需要明确地指定对象的各个组成部分。简单来说工厂是“给我一个东西”Builder是“我要这样一个东西它有A、B、C属性”。前者强调隐藏创建逻辑后者强调清晰表达配置。4.2 Builder模式 vs. 变种Step Builder当对象的构建过程有严格的顺序要求时经典Builder可能还不够。例如创建一份订单你必须先有商品才能设置数量然后才能选择配送地址。这时可以使用Step Builder。Step Builder通过返回不同的接口类型来强制构建步骤的顺序。下面是一个简化的概念示例public interface OrderStep { QuantityStep item(String itemId); } public interface QuantityStep { AddressStep quantity(int quantity); } public interface AddressStep { BuildStep address(String address); } public interface BuildStep { Order build(); } public class OrderBuilder implements OrderStep, QuantityStep, AddressStep, BuildStep { // ... 实现每个步骤的方法每个方法返回下一个步骤的接口 public static OrderStep newBuilder() { return new OrderBuilder(); } }使用方式被强制为Order order OrderBuilder.newBuilder() .item(ITEM_001) // 必须先调用 .quantity(2) // 然后才能调用 .address(Some Address) // 接着才能调用 .build(); // 最后构建这种模式在领域驱动设计DDD或流程明确的场景中非常有用它能通过编译器来保证业务规则的执行顺序。4.3 性能与内存开销真的需要考虑吗这是一个很实际的问题多写一个Builder内部类多创建了一个Builder对象会不会有性能问题在绝大多数应用场景下这个开销完全可以忽略不计。Builder对象的创建和回收成本极低现代JVM的垃圾回收器尤其是针对短生命周期对象的Young GC效率非常高。与之相比代码的可读性、可维护性和健壮性带来的收益是巨大的、长期的。只有在极端性能敏感的场景例如在循环中每秒要创建数百万个简单对象并且经过实际性能剖析Profiling证实对象创建是瓶颈时才需要考虑不使用Builder。对于99.9%的业务系统、Web应用、中间件来说Builder模式带来的那点微乎其微的性能损耗远不值得你牺牲代码质量。一个更值得关注的“性能”问题是防止滥用。不要为只有两三个简单属性的类使用Builder那会显得过度设计让简单问题复杂化。通常当构造器参数超过4个或者未来有很大可能增加参数时就是引入Builder的好时机。4.4 一个真实的“踩坑”案例默认值覆盖问题让我们看一个我亲身经历的坑。我们有一个配置类使用Lombok的Builder其中有一个重试次数retryCount我们期望默认值是3。Builder public class ClientConfig { Builder.Default private int retryCount 3; private String endpoint; }在代码中我们这样使用ClientConfig config ClientConfig.builder() .endpoint(http://api.example.com) .build(); System.out.println(config.getRetryCount()); // 输出3 符合预期后来另一个同事在另一个地方需要创建一个配置他“复制粘贴”了这段代码但觉得重试次数应该是5于是他“顺手”改了一下ClientConfig config ClientConfig.builder() .endpoint(http://api.example.com) .retryCount(5) // 明确设置为5 .build(); System.out.println(config.getRetryCount()); // 输出5 符合预期这看起来都没问题。但坑出现在一次代码重构中。有人觉得retryCount这个名字不准确改成了maxRetries。他使用了IDE的重构功能重命名了字段和getter方法。但是Lombok生成的Builder方法名retryCount()并没有被自动重命名因为它是编译时生成的IDE的重构可能检测不到。于是代码变成了Builder public class ClientConfig { Builder.Default private int maxRetries 3; // 字段名改了 private String endpoint; // Lombok生成的方法名还是 retryCount() } // 调用方的代码编译报错找不到符号 retryCount() ClientConfig config ClientConfig.builder() .endpoint(http://api.example.com) .retryCount(5) // 这里报错 .build();这个问题的根源在于Builder的方法名与字段名是解耦的虽然默认相同。当字段名改变时调用方的代码会断裂。而如果使用的是传统的setter或构造器参数IDE的重构可以安全地全局修改。教训当使用LombokBuilder时如果修改了带有Builder.Default注解的字段名必须手动检查并更新所有调用builder()方法的地方。更好的实践是对于可能变化的配置项考虑将其放入一个单独的、不变的配置对象如Map或Properties中或者在使用Builder时对关键配置项添加清晰的文档注释并在重构后进行全面的搜索和测试。对于手动实现的Builder由于方法名是你自己写的重构工具通常能更好地处理。

相关新闻

免费开源的英雄联盟战绩查询工具 Seraphine:3步配置,让排位决策更轻松

免费开源的英雄联盟战绩查询工具 Seraphine:3步配置,让排位决策更轻松

免费开源的英雄联盟战绩查询工具 Seraphine:3步配置,让排位决策更轻松 【免费下载链接】Seraphine 英雄联盟战绩查询工具 项目地址: https://gitcode.com/gh_mirrors/se/Seraphine 还在为排位赛前摸不清队友实力而心里打鼓?还在禁用环…

2026/8/16 11:16:54 阅读更多 →
Google三篇论文如何奠定大数据基石:GFS、MapReduce与BigTable解析

Google三篇论文如何奠定大数据基石:GFS、MapReduce与BigTable解析

1. 从“不可能”到“日常”:三篇论文如何重塑数据处理的世界如果你今天在任何一个技术社区里提到“大数据”,几乎所有人都会默认你指的是以Hadoop、Spark为代表的那一套分布式处理体系。但时间倒退回21世纪初,情况却截然不同。那时&#xff0…

2026/8/16 11:02:05 阅读更多 →
基于李代数的PnP优化:从数学原理到视觉SLAM工程实践

基于李代数的PnP优化:从数学原理到视觉SLAM工程实践

1. 项目概述:当PnP遇上李代数在计算机视觉和机器人领域,估计相机相对于三维世界的位置和姿态(即相机位姿)是一个基础且核心的问题。Perspective-n-Point(PnP)问题就是其中的典型代表:给定一组三…

2026/8/15 9:06:19 阅读更多 →

最新新闻

PPT布尔运算:从图形加减到设计进阶的底层逻辑与实战

PPT布尔运算:从图形加减到设计进阶的底层逻辑与实战

1. 项目概述:被低估的PPT设计“核武器” 如果你经常在网上看一些设计大神分享的PPT作品,或者研究过一些顶尖咨询公司的报告,一定会被那种干净、利落、充满高级感的图形所吸引。那些看似简单的图标、富有层次感的文字效果、极具创意的数据图表…

2026/8/16 11:36:11 阅读更多 →
飞芯硬件开发实战:选型避坑、电源设计、射频调试与低功耗优化

飞芯硬件开发实战:选型避坑、电源设计、射频调试与低功耗优化

1. 项目缘起:为什么我们需要一个“飞芯群”知识库 在硬件开发,特别是涉及射频、模拟电路和传感器设计的圈子里,有一个词最近被频繁提起,那就是“飞芯”。这个词听起来有点“黑话”的味道,但它精准地指向了一类特殊的集…

2026/8/16 11:36:11 阅读更多 →
KMS_VL_ALL_AIO完全指南:3种激活模式彻底解决Windows和Office激活难题

KMS_VL_ALL_AIO完全指南:3种激活模式彻底解决Windows和Office激活难题

KMS_VL_ALL_AIO完全指南:3种激活模式彻底解决Windows和Office激活难题 【免费下载链接】KMS_VL_ALL_AIO Smart Activation Script 项目地址: https://gitcode.com/gh_mirrors/km/KMS_VL_ALL_AIO 早上十点,你正准备把方案发给客户,Offi…

2026/8/16 11:36:11 阅读更多 →
Deepseek Harness:AI智能体标准化评估平台核心功能与实战指南

Deepseek Harness:AI智能体标准化评估平台核心功能与实战指南

Deepseek Harness 正式上线了,这个由深度求索(DeepSeek)官方推出的AI智能体开发与评估平台,在X(原Twitter)上引发了开发者和AI研究者的热烈讨论。一个有趣的现象是,其英文官方账号和中文官方账号…

2026/8/16 11:36:11 阅读更多 →
免费NTFS读写工具踩坑日记:Nigate 把 Mac 上那块“只读“的 Windows 硬盘救活了

免费NTFS读写工具踩坑日记:Nigate 把 Mac 上那块“只读“的 Windows 硬盘救活了

免费NTFS读写工具踩坑日记:Nigate 把 Mac 上那块"只读"的 Windows 硬盘救活了 【免费下载链接】Free-NTFS-for-Mac Nigate: An open-source NTFS utility for Mac. It supports all Mac models (Intel and Apple Silicon), providing full read-write acc…

2026/8/16 11:36:11 阅读更多 →
Claude Code 保姆级教程:从零上手AI编程助手,提升开发效率

Claude Code 保姆级教程:从零上手AI编程助手,提升开发效率

大家好,我是专注于分享开发实战与工具提效的博主。在探索AI编程助手的路上,你是否也遇到过这样的困扰:面对琳琅满目的工具,安装配置步骤繁琐,网上教程零散不成体系,好不容易装上了,却不知道如何…

2026/8/16 11:35:11 阅读更多 →

日新闻

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

如果你是一名开发者,最近可能已经感受到了AI大模型正在从“玩具”变成“生产力工具”的强烈信号。从代码补全到智能Agent,从本地部署到云端API,我们正处在一个技术栈快速重构的节点。然而,面对层出不穷的模型、框架和工具&#xf…

2026/8/16 0:00:54 阅读更多 →
工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

第四篇:反射——高频能量撞墙之后会发生什么? —— 你以为信号已经过去了,其实它正在回来打你 老Q的现场笔记 第五季,我们正式进入工业神经系统层。这里不再是单个设备的战斗,而是整个工厂“经脉”层面的秩序之战。从这一篇开始,你将第一次看清:看似简单的信号传播,背…

2026/8/16 0:00:55 阅读更多 →
【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室👇 关注我领取海量matlab电子书和…

2026/8/16 0:03:55 阅读更多 →

周新闻

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

如果你是一名开发者,最近可能已经感受到了AI大模型正在从“玩具”变成“生产力工具”的强烈信号。从代码补全到智能Agent,从本地部署到云端API,我们正处在一个技术栈快速重构的节点。然而,面对层出不穷的模型、框架和工具&#xf…

2026/8/16 0:00:54 阅读更多 →
工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

第四篇:反射——高频能量撞墙之后会发生什么? —— 你以为信号已经过去了,其实它正在回来打你 老Q的现场笔记 第五季,我们正式进入工业神经系统层。这里不再是单个设备的战斗,而是整个工厂“经脉”层面的秩序之战。从这一篇开始,你将第一次看清:看似简单的信号传播,背…

2026/8/16 0:00:55 阅读更多 →
【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室👇 关注我领取海量matlab电子书和…

2026/8/16 0:03:55 阅读更多 →

月新闻

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

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

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

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

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

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

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

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

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

2026/8/16 6:00:27 阅读更多 →