news 2026/9/9 11:31:50

Java 11新特性实战指南:核心API、HttpClient与迁移避坑

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java 11新特性实战指南:核心API、HttpClient与迁移避坑

Java 11 这个版本,放在整个Java生态里都是一个绕不开的转折点。它既是Java 8之后真正意义上的长期支持版本(LTS),又第一次把Oracle JDK的免费许可开放到了个人开发和多数生产场景。很多人问我Java 11有哪些新特性,我自己的回答是:别只盯着语法糖,先把字符串API、HttpClient、模块化边界和垃圾回收器这四块吃透,再考虑从Java 8平滑迁移,这才是这个版本真正的价值所在。

这篇文章不打算逐条念官方Release Notes,我想以实际使用的视角,把Java 11里我用过、踩过坑、后来沉淀成团队规范的东西一次讲透。适合正准备升级的老项目、正在选型新项目基础版本的同学,以及想在Windows 10上第一次装好Java 11、把环境变量配置顺手搞定的小白。

1. Java 11到底新在哪:先建立整体认知

1.1 为什么Java 11是最不能错过的版本

Java 11发布于2018年9月,但它的地位放到今天依然很特殊。Java 8虽然普及率极高,但已经是很老的版本,而Java 9和Java 10都是“短命”版本,官方不提供长期维护,企业根本不敢把生产环境压上去。Java 11是Oracle在模块化大改动之后给出的第一个真正稳定的长期支持版本,维护周期覆盖很多年,这才让大批团队第一次有了“从Java 8往前迈一步”的理由。

另一个关键变化是许可模式。Oracle JDK从11开始,在OTN许可协议下可以免费用于开发、测试和一些生产场景,这就打破了过去“商用要付费”的普遍顾虑。当然,这里要提醒一句:如果你的场景在许可边界上拿不准,最稳妥的做法是直接使用OpenJDK发行版,比如Adoptium Temurin、Amazon Corretto、Microsoft OpenJDK,功能一致且没有商业协议负担。我团队里的新项目现在基本都是Temurin 11起步,省心。

1.2 新特性全景一览

把Java 11涉及的JEP快速过一遍,能帮你在后续阅读官方文档时建立索引。我按使用角度重新分组,比按JEP编号罗列更贴近实际工作。

分组核心内容日常影响
语法/APIString新增isBlank、strip、repeat、lines;Collection新增toArray(IntFunction);Files新增readString和writeString代码量直接变少,可读性提升
集合/StreamPredicate.not;Stream.takeWhile、dropWhile;List.of、Set.of、Map.of写过滤和构造集合时最常用
类型推断var关键字继续完善,lambda参数也支持var声明局部变量更简洁,但过度使用会伤可读性
网络编程HttpClient正式标准化,支持HTTP/2、WebSocket告别原生HttpURLConnection,小工具不再强依赖第三方库
运行时/GCG1成为默认垃圾回收器;ZGC、Epsilon进入实验阶段大内存低延迟场景多了选择
兼容性Java EE模块被移除、CORBA移除、JavaFX移除、Nashorn废弃老项目升级最容易在这里爆雷
启动/部署支持直接运行单文件Java源码;AppCDS应用类数据共享增强写脚本和微服务启动调优更方便
安全/协议TLS 1.3默认支持网络安全合规方面的硬需求

看完这张表你会发现,Java 11不像Java 8当年那样是“翻天覆地”的语法革命,它更像一次全面地“清扫和补强”。上承Java 9的模块化和Java 10的var,下接后续版本的持续演进,Java 11是把前面版本欠的债还完、把积攒的功能打磨到可用的关键节点。

2. 日常开发最用得上的新特性

2.1 字符串API增强:isBlank、strip、repeat、lines

字符串API是Java 11里我使用频率最高的新特性,没有之一。先看一段最直接的演示:

String name = " hello "; System.out.println(name.strip()); // hello System.out.println(name.stripLeading()); // "hello " System.out.println(name.stripTrailing()); // " hello" System.out.println("abc".repeat(3)); // abcabcabc System.out.println(" ".isBlank()); // true System.out.println("a\nb\nc".lines().count()); // 3

这里有个容易踩的坑:很多人觉得strip就是trim的别名,其实strip是基于Character.isWhitespace判断的,它能处理中文全角空格这种Unicode空白,而trim只处理码点小于等于U+0020的字符。换句话说,如果用户输入里混了全角空格,trim根本去不掉,strip才能干净处理。我在做表单校验时已经全面切到strip。

lines()方法也很实用。以前要把多行文本拆成List,得自己按换行符split,还要处理换行格式差异。现在直接text.lines().toList()即可,内部已经兼容\n\r\n\r三种换行。注意这里的toList是Java 16才加到Stream上的,在Java 11里应该写collect(Collectors.toList()),别在JDK 11环境下拿Stream.toList直接编译,编译期就会报错。

2.2 集合与Stream的便利更新

List.of、Set.of、Map.of是Java 9引入的,但在Java 11这个LTS版本里才被更多人放心使用。它们返回的是不可变集合,最大的价值是把“创建带初始值的集合”从多行代码压缩成一行:

List<String> names = List.of("张三", "李四", "王五"); Set<Integer> ids = Set.of(1, 2, 3); Map<String, Integer> score = Map.of("语文", 90, "数学", 95);

用这几个方法要注意三点:

  • 不能向返回的集合里添加元素,否则抛UnsupportedOperationException。
  • 集合中不允许null元素,Map也不允许null key和null value。
  • Map.of最多支持10对键值,超过10个要用Map.ofEntries。

Stream在Java 11里也有两个高频操作:takeWhile和dropWhile。它们和filter最大的区别是:filter会遍历整个流,而takeWhile遇到第一个不满足条件的元素就立刻截断,dropWhile则跳过开头满足条件的元素。最适合处理有序数据,比如日志中时间是递增的,想取最近10分钟的数据就可以用takeWhile来快速短路,避免无谓的全量遍历。

另外,Predicate.not是Java 11新增的静态方法。以前写“取反过滤”要么用!包一层,要么单独写个方法引用,现在可以直接:

Stream.of("", "hello", " ", "world") .filter(Predicate.not(String::isBlank)) .forEach(System.out::println);

配合刚说的isBlank,这行代码在清洗输入数据时极其好用。

2.3 var说起来简单,但有几个细节要记住

var是Java 10引入的局部变量类型推断,Java 11里补上了“lambda参数也支持var”这块拼图。最常见的写法:

var list = new ArrayList<String>(); list.add("test"); list.forEach((var s) -> System.out.println(s));

lambda参数使用var有一个关键限制:要么全部参数都写var,要么全部不写,不能混合。它真正的应用场景是需要给lambda参数加注解时,以前必须显式声明类型,现在可以写成:

list.forEach((@NotNull var s) -> System.out.println(s));

在使用var时我给自己定了三条纪律:

  • 能用清晰类型声明的地方,不强行用var。例如var user = getUser()这种就没有任何信息量。
  • 变量名必须起得足够明确,因为类型被隐藏后,名字就是读者唯一的线索。
  • 绝不用var声明局部变量后再改变它的实际类型,这会让代码变成“跟踪谜题”。

var并不是关键字,它更像一个“保留类型名”,所以var var = 1这种代码其实是合法的,只是可读性极差。还有几个常见误区:var不能用于字段、方法参数和返回类型,不能声明数组var[] arr,不能在声明时不初始化var x;,也不能初始化为null。这些在编译期就会报错,熟悉一下即可。

3. 升级Java 11前必须知道的兼容性和移除项

3.1 Java EE模块和CORBA被移除

这是老项目从Java 8升级到Java 11时最大的拦路虎。JDK 11直接移除了java.se.ee根模块下的六个模块:JAXB、JAX-WS、JAF、JTA、CORBA,还有JavaFX也单独拆出去独立发布了。换句话说,如果你的老项目直接在代码里用了javax.xml.bind.JAXBContext这类类,升级后编译期就会报ClassNotFoundException。

我记得当时迁移一个老服务,启动后立刻爆ClassNotFoundException: javax.xml.bind.JAXBException,排查了半天才发现是XML配置解析用到了JAXB。解决办法是在pom里显式引入依赖:

<dependency> <groupId>jakarta.xml.bind</groupId> <artifactId>jakarta.xml.bind-api</artifactId> <version>2.3.3</version> </dependency> <dependency> <groupId>org.glassfish.jaxb</groupId> <artifactId>jaxb-runtime</artifactId> <version>2.3.3</version> </dependency>

这里特别注意groupId和包名:新版本的Jakarta EE会把javax.xml.bind迁移到jakarta.xml.bind,但JAXB 2.3.3及之前的版本包名仍然是javax.xml.bind,如果你的代码改动越少越好,就用2.3.x版本。如果后续要用Jakarta EE 9+,包名会变成jakarta,代码里的import也要跟着改。这个选择直接影响工作量。

3.2 G1成为默认,CMS正式告别

从Java 9开始,G1就是默认垃圾回收器,Java 11继续沿用。CMS在Java 9被标记废弃,Java 14才会被真正移除,但在Java 11里如果还显式指定CMS启动,会看到大量警告信息。对于大多数Web应用,G1的停顿时间模型比ParallelGC更适合业务场景,它能设置期望的GC停顿目标,默认是200毫秒,常用参数是-XX:MaxGCPauseMillis=100

我个人的建议是:小内存应用(堆在2GB以内)可以继续使用默认G1,没必要折腾;大内存、低延迟场景可以研究ZGC,但要先做好压测。这里也提醒一句,GC参数的调整一定要有监控数据支撑,不要凭感觉加参数。很多人升级到Java 11后第一件事就是把CMS参数复制过来,结果启动直接报错,因为CMS相关参数在Java 11已经不再生效。升级时一定要清掉-XX:+UseConcMarkSweepGC这类老参数。

3.3 ZGC和Epsilon:面向未来的试验性GC

ZGC是Java 11引入的实验性垃圾回收器,目标是让GC停顿时间不随堆大小增长,号称STW时间控制在10毫秒以内。它通过染色指针和读屏障实现并发标记、并发转移,适合堆内存动辄几十GB、又要低延迟的服务。

想试ZGC很简单,加两个参数:

-XX:+UnlockExperimentalVMOptions -XX:+UseZGC

但请务必加上“实验性”的心态看待它。我实测过ZGC在超大堆场景下的确亮眼,但它在Java 11里还不支持某些功能,比如类卸载在某些配置下不完整,而且CPU占用会比G1高。生产环境如果要上ZGC,最好等到Java 15之后的版本,功能更成熟。Epsilon则是一个“不回收垃圾”的GC,只做内存分配,主要用于性能测试和短暂任务,生产环境基本不会用它。

3.4 直接运行Java源文件:单文件源码启动

JEP 330让Java 11可以直接通过java Xxx.java运行单个源文件,不用先javac编译。这对写小工具、脚本、教学demo非常友好,再也不用为一个小文件折腾编译参数。

java Hello.java

这里有一个实际限制:该模式只适合“单文件源码级”运行,如果你的程序依赖classpath上的第三方jar包,或者跨文件引用多个java文件,还是需要正常编译。而且它没有把源码编译输出到磁盘,每次运行都会临时编译,性能上不划算。我的经验是:写算法题、跑测试片段、临时脚本用这个功能很爽,但正式项目千万别用这种启动方式,别人接手会懵。

4. HttpClient:Java 11最值得兴奋的新API

4.1 同步请求:三行代码搞定HTTP调用

Java 11把HttpClient从孵化器转为标准API,这真的是很多Java开发等了好多年的功能。以前用原生HttpURLConnection写请求又啰嗦又难用,只能依赖Apache HttpClient或OkHttp。现在JDK自带的就能完成大部分工作。

先看一个最基础的GET请求:

import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.time.Duration; HttpClient client = HttpClient.newBuilder() .connectTimeout(Duration.ofSeconds(10)) .build(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.example.com/user/1")) .timeout(Duration.ofSeconds(30)) .header("Accept", "application/json") .GET() .build(); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.statusCode()); System.out.println(response.body());

这基本上是“开箱即用”的体验。请求体的设置也很直观,POST JSON可以这样:

HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.example.com/user")) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString("{\"name\":\"张三\"}")) .build();

这里我想强调一下BodyHandlersBodyPublishers的设计。它们是响应和请求的“Body处理器”,除了ofString,还有ofByteArrayofFileofInputStreamofLines等选择。比如要下载文件,用BodyHandlers.ofFile(Paths.get("xxx.zip"))即可,不需要手动处理流的生命周期。

4.2 异步请求:用CompletableFuture做并发

HttpClient真正的优势在于异步。它基于CompletableFuture实现,不阻塞当前线程,在高并发调用场景下比同步发送要高效得多:

HttpClient client = HttpClient.newBuilder() .executor(Executors.newFixedThreadPool(10)) .build(); List<HttpRequest> requests = buildRequests(); // 构造多个请求 List<CompletableFuture<HttpResponse<String>>> futures = requests.stream() .map(req -> client.sendAsync(req, HttpResponse.BodyHandlers.ofString())) .collect(Collectors.toList()); CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) .join(); for (CompletableFuture<HttpResponse<String>> future : futures) { System.out.println(future.join().body()); }

这里有个隐性问题:CompletableFuture默认使用ForkJoinPool.commonPool,线程数等于CPU核心数。如果你的业务是IO密集型,比如要并发调用十几个下游接口,默认线程池很容易成为瓶颈。建议通过HttpClient.newBuilder().executor(...)显式指定一个线程池,或者在最终join()时配置好超时时间,避免某个下游接口迟迟不响应导致整个调用链卡死。

我写异步调用时还有一个习惯:对每个future都做exceptionally处理,把异常转换为一个默认结果或错误包装。否则一个接口异常虽然不会中断其他请求,但最后的join会直接抛出CompletionException,排错时很难定位是哪个请求出了问题。

4.3 WebSocket客户端和连接管理

Java 11的HttpClient还内置了WebSocket客户端,不需要再引第三方库。最基础的用法:

HttpClient client = HttpClient.newHttpClient(); WebSocket webSocket = client.newWebSocketBuilder() .buildAsync(URI.create("ws://localhost:8080/ws"), new WebSocket.Listener() { @Override public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) { System.out.println("收到消息: " + data); return null; } }) .join(); webSocket.sendText("hello", true);

这里有一个细节:WebSocket.Listener的每个回调都返回CompletionStage,目的是实现背压控制——只有前一个CompletionStage完成,才会继续触发下一个回调。这是Java 11 WebSocket和很多简单封装的第三方库不一样的地方。如果你返回null,相当于立刻完成,不控制速度。在生产环境处理大量实时消息时,这个机制很有价值。

5. Java 11下载与Windows 10环境变量配置指南

5.1 下载前先选对JDK发行版

很多人一搜“Java 11下载”就直接进了Oracle官网,其实现在选择已经很多了。我的建议是:开发环境用Oracle JDK或Temurin都行,生产环境优先OpenJDK系发行版,比如Temurin、Corretto,理由就是许可清晰、更新及时、社区验证充分。

如果你在Windows 10上安装,下载时注意两个东西:一是要选对架构,现在基本是x64,但有人拿32位安装包装64位系统就会莫名报错;二是要区分JDK和JRE,Java 11开始Oracle不再单独发布JRE安装包,如果你想运行Java程序,直接安装完整JDK即可。下载之后得到的是exe安装包或者zip压缩包,zip版解压后配置环境变量就能用,也方便做多版本切换。

5.2 Windows 10安装与配置JAVA_HOME

如果使用exe安装,一路Next即可,但要注意安装在哪个目录,后面配置环境变量会用到。如果是zip包,先解压到一个干净目录,比如C:\Program Files\Java\jdk-11.0.21,然后按下面步骤操作:

  1. 右键“此电脑”,选择“属性”,点击“高级系统设置”。
  2. 点击“环境变量”,在“系统变量”区域点击“新建”。
  3. 变量名填写JAVA_HOME,变量值填写JDK安装路径,例如C:\Program Files\Java\jdk-11.0.21,注意不要带bin目录。
  4. 在系统变量里找到Path,选中后点击“编辑”,点击“新建”,添加%JAVA_HOME%\bin
  5. %JAVA_HOME%\bin通过“上移”按钮移到最上面,避免和其他JDK版本冲突。
  6. 一路确定保存,然后重新打开一个命令行窗口。

这里特别强调一个容易出问题的地方:JAVA_HOME的值一定不要写成C:\Program Files\Java\jdk-11.0.21\bin,配置的是JDK根目录,Path里才拼bin。很多教程都栽在这个细节上。

还有一些开发工具需要单独设置。IDEA里可以在Project Structure里选JDK,Eclipse在Installed JREs里添加,Maven的settings.xml里如果硬编码了JAVA_HOME也要同步修改。配置环境变量只是第一步,工具的JDK路径不更新,系统命令是11,IDE编译还是8,这种情况很常见。

5.3 验证配置和常见配置错误

配好后重新打开命令行窗口,依次输入:

java -version javac -version echo %JAVA_HOME% where java

期望看到类似:

java version "11.0.21" 2023-10-17 LTS javac 11.0.21 JAVA_HOME=C:\Program Files\Java\jdk-11.0.21

我遇到过几次典型问题,先列出来供你快速定位:

现象原因解决方法
提示“java不是内部或外部命令”JAVA_HOME或Path没有配置正确检查JAVA_HOME路径是否存在、Path是否包含%JAVA_HOME%\bin
java -version还是8旧版本的bin目录排在Path更前面把新JDK的bin目录移到Path最上方,或卸载旧JDK
javac有输出但java显示其他版本系统中存在多个JDK,java.exe路径不对用where java查看实际匹配路径,清理PATH中的旧项
重启后命令窗口仍不生效环境变量修改后需要重开终端重新打开cmd,不要使用修改前已打开的窗口

如果验证通过,可以顺手写个Hello World测试一下编译和运行是否正常:

echo public class Main { public static void main(String[] args) { System.out.println("Java11 OK"); } } > Main.java javac Main.java java Main

6. 迁移到Java 11的实战避坑与心得

6.1 常见问题速查表

这节是把Java 8项目迁移到Java 11后,我实际遇到并帮助同事解决过的问题汇总:

异常/现象根因解决方案
ClassNotFoundException: javax.xml.bind.JAXBExceptionJAXB从JDK移除引入jaxb-api和jaxb-runtime依赖
ClassNotFoundException: javax.annotation.PostConstructJSR-250注解不在默认模块引入jakarta.annotation-api依赖
java.lang.NoClassDefFoundError: javax/activation/DataHandlerJAF模块被移除引入javax.activation:activation依赖
Unsupported major version 55class文件由Java 11编译,运行环境是旧JDK运行时升级到JDK 11,或降低编译release版本
--add-opens相关警告模块化后反射访问限制明确使用add-opens开放指定包
编译时报“不兼容的 types”代码中误用已移除API全局搜索javax.xml、javax.ws等包名逐一替换
JavaFX应用启动失败JavaFX已从JDK移除单独引入OpenJFX依赖或改用独立版本

这里最关键的经验是:迁移前先做一次“依赖审计”。把pom.xml和所有lib目录里的jar包扫一遍,凡是涉及javax.annotation、javax.xml.bind、javax.jws、javax.transaction的,大概率都要补依赖。这一步提前做了,后面能少加一晚上的班。

6.2 哪些新特性优先用,哪些先观望

我给团队定的Java 11特性采用策略是分层的:

第一梯队,立刻用:String.strip和isBlank、Files.readString和writeString、List.of/Set.of/Map.of、Predicate.not、HttpClient的新项目优先使用。这些API无副作用、学习成本低、收益明确。

第二梯队,看场景用:var在局部变量和lambda参数中使用,但要求代码评审严格把关;单文件源码启动只用于脚本和工具;HTTP/2在内部服务间如果基础设施支持,值得开启。

第三梯队,先观望:ZGC和Epsilon这类实验性GC,必须经过充分压测和监控验证后才考虑;直接源码运行不适合生产;模块化相关的高级用法,除非你是做框架或基础组件,否则不必为了模块化而模块化。

6.3 由一次迁移引发的个人体会

从Java 8迁到Java 11,我最大的感受是“惊喜比预想的多,坑也比预想的集中”。惊喜在于:代码确实变短了,HttpClient写起来顺手,G1默认配置的GC表现比预期稳。坑则集中在移除模块上,基本都是ClassNotFoundException,而且往往要到运行期才炸出来。

后来摸索出一套稳定的迁移流程:先补依赖,再改代码,最后跑回归。补依赖阶段把jaxb、annotation、activation这些老库全部显式声明;改代码阶段用全局搜索把javax.xml等相关import挨个替换;跑回归时重点测XML解析、WebService调用、反射工具和启动脚本。整个流程走通后,老项目升级Java 11的成本其实比想象中低,收益却非常实在。

最后再分享一个小技巧:如果短期内没法完整迁移,可以用--release 8参数让Java 11编译器输出Java 8版本的字节码,这样开发环境先切换到JDK 11,产出的class文件仍然能被Java 8运行时加载。这个参数可以让团队在“还没彻底升级生产”的过渡期,提前享受新版编译器和工具体验,为真正的迁移争取时间。先跑起来再逐步替换老API,是我实际用下来最平滑的方式。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/9 11:31:38

服务器内存ECC纠错与MBIST自测:从原理到运维排查指南

做过几年服务器和底层硬件相关的工作&#xff0c;对“ECC”这三个字母可以说是又爱又恨。爱它&#xff0c;是因为服务器内存一旦开启ECC纠错&#xff0c;很多偶发的软错误能直接被硬件自动抹掉&#xff0c;系统不会莫名其妙宕机&#xff1b;恨它&#xff0c;是因为只要日志里出…

作者头像 李华
网站建设 2026/9/9 11:31:13

Java11 LTS核心特性与升级迁移实战指南

1. Java11的定位&#xff1a;为什么它是绕不开的版本 1.1 LTS版本意味着什么 聊Java11之前&#xff0c;得先把它放回JDK的发布历史里看。Java8之后的版本节奏变化很大&#xff0c;Oracle把发布周期改成了半年一次&#xff0c;Java9、Java10都是快速迭代的过渡版本&#xff0c;…

作者头像 李华
网站建设 2026/9/9 11:30:34

ToDesk企业级远程协作深度评测:从安全密码到CLI自动化运维全解析

这几年做企业信息化和 IT 运维&#xff0c;我经手过不少远程控制工具的选型&#xff0c;从个人版免费软件到商业级方案几乎都摸过一遍。2026 年再回头看&#xff0c;ToDesk 在企业级远程协作这个赛道里确实已经站稳了头部位置&#xff0c;不只是“能用”&#xff0c;而是真正能…

作者头像 李华
网站建设 2026/9/9 11:27:44

多边形拆分算法详解:耳切法三角剖分与凸分解实践

“多边形拆分”这个需求&#xff0c;最初是在做一套游戏编辑器工具时被逼出来的。美术在场景里用鼠标随手画了一个不规则的石头形状&#xff0c;离线生成碰撞体的时候&#xff0c;物理引擎压根不吃凹多边形&#xff0c;碰撞体直接变成一个大包围盒&#xff0c;游戏里手感怪到没…

作者头像 李华
网站建设 2026/9/9 11:25:46

Terraform管理OIDC身份提供商:VCFA门户接入实战

上周接到一个活儿&#xff1a;把公司 VCFA 组织门户的登录从原来的本地账号体系&#xff0c;整体切换到 OIDC 身份提供商。任务本身听起来不复杂&#xff0c;难的是领导压了一句"全程用 Terraform 来配置&#xff0c;不许在控制台里手动点"。我当时第一反应是有点小题…

作者头像 李华
网站建设 2026/9/9 11:25:44

C#上位机蓝牙通信实战:32feet.net与RFCOMM虚拟串口

简介&#xff1a;这是面向C#开发者的蓝牙开发源码资料包&#xff0c;主体为32feet.net与InTheHand库的完整实现&#xff0c;覆盖蓝牙设备发现、RFCOMM/L2CAP通信、BLE广播扫描与连接管理等核心功能&#xff0c;并支持低功耗BLE应用场景&#xff0c;适合嵌入式、物联网及移动应用…

作者头像 李华