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

浅谈 Guava 中的 ImmutableMap.of 方法的坑

作者:明明如月学长, CSDN 博客专家,大厂高级 Java 工程师,《性能优化方法论》作者、《解锁大厂思维:剖析《阿里巴巴Java开发手册》》、《再学经典:《EffectiveJava》独家解析》专栏作者。

热门文章推荐

  • (1)《为什么很多人工作 3 年 却只有 1 年经验?》
  • (2)《从失望到精通:AI 大模型的掌握与运用技巧》
  • (3)《AI 时代,程序员的出路在何方?》
  • (4)《如何写出高质量的文章:从战略到战术》
  • (5)《我的技术学习方法论》
  • (6)《我的性能方法论》
  • (7)《AI 时代的学习方式: 和文档对话》

一、背景

Guava 的 ImmutableMap类提供了 of方法,可以很方便地构造不可变 Map。

 ImmutableMap<String, Integer> build = ImmutableMap.of("a",1,"b",2);

然而,实际工作开发中很多人会从开始认为非常方便,后面到发现很多大家都会遇到相似的“问题”。
比如 ImmutableMap类的 of 存在很多重载的方法,但是最多只有五个键值对。
有无参的方法:

  /*** Returns the empty map. This map behaves and performs comparably to {@link* Collections#emptyMap}, and is preferable mainly for consistency and maintainability of your* code.** <p><b>Performance note:</b> the instance returned is a singleton.*/@SuppressWarnings("unchecked")public static <K, V> ImmutableMap<K, V> of() {return (ImmutableMap<K, V>) RegularImmutableMap.EMPTY;}

有支持一个键值对的方法:

  /*** Returns an immutable map containing a single entry. This map behaves and performs comparably to* {@link Collections#singletonMap} but will not accept a null key or value. It is preferable* mainly for consistency and maintainability of your code.*/public static <K, V> ImmutableMap<K, V> of(K k1, V v1) {return ImmutableBiMap.of(k1, v1);}

到支持五个键值对的方法:

  /*** Returns an immutable map containing the given entries, in order.** @throws IllegalArgumentException if duplicate keys are provided*/public static <K, V> ImmutableMap<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) {return RegularImmutableMap.fromEntries(entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3), entryOf(k4, v4), entryOf(k5, v5));}

很多人会遇到的坑:

  • 超过五个键值对怎么办?
  • key 和 value “居然”都不能为 null?
  • 同一个 key 重复 put 报错

二、场景还原

2.1 超过 5 个键值对问题

虽然 of 方法很好用,但是经常会遇到超过 5 个键值对的情况,就非常不方便。

解法1:升级版本

在 guava 31.0 版本以后,已经拓展到了 10 个键值对!

  /*** Returns an immutable map containing the given entries, in order.** @throws IllegalArgumentException if duplicate keys are provided* @since 31.0*/public static <K, V> ImmutableMap<K, V> of(K k1,V v1,K k2,V v2,K k3,V v3,K k4,V v4,K k5,V v5,K k6,V v6,K k7,V v7,K k8,V v8,K k9,V v9,K k10,V v10) {return RegularImmutableMap.fromEntries(entryOf(k1, v1),entryOf(k2, v2),entryOf(k3, v3),entryOf(k4, v4),entryOf(k5, v5),entryOf(k6, v6),entryOf(k7, v7),entryOf(k8, v8),entryOf(k9, v9),entryOf(k10, v10));}

解法2:使用 builder 方法

com.google.common.collect.ImmutableMap#builder 方法可以通过构造器的方式不断 put 键值对,最后 build即可,也非常方便。

      ImmutableMap<String, Integer> build = ImmutableMap.<String, Integer>builder().put("a", 1).put("b", 2).put("c", 3).put("d",4).put("e",5).put("f",6).build();

也可以参考 2.2 中的解法。

2.2 键值都不允许为 null

复现

很多人看到名字就知道不可“修改” 但不太清楚它的键值都不允许为 null。

key 为空的情况:
在这里插入图片描述

value 为空的情况:
在这里插入图片描述

真正开发时不会那么简单,有时候需要调用某个接口获取返回值然后再构造一个不可编辑的 Map 返回给下游使用。很可能在测试的时候都没有出现 null 值,发布上线,发现 key 或者 value 为 null,就会造成线上问题 或者 bug。

源码

对于 of的多参数重载:

  /*** Returns an immutable map containing the given entries, in order.** @throws IllegalArgumentException if duplicate keys are provided*/public static <K, V> ImmutableMap<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3) {return RegularImmutableMap.fromEntries(entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3));}
  /*** Verifies that {@code key} and {@code value} are non-null, and returns a new immutable entry* with those values.** <p>A call to {@link Entry#setValue} on the returned entry will always throw {@link* UnsupportedOperationException}.*/static <K, V> Entry<K, V> entryOf(K key, V value) {return new ImmutableMapEntry<>(key, value);}
  ImmutableMapEntry(K key, V value) {super(key, value);checkEntryNotNull(key, value);}
  static void checkEntryNotNull(Object key, Object value) {if (key == null) {throw new NullPointerException("null key in entry: null=" + value);} else if (value == null) {throw new NullPointerException("null value in entry: " + key + "=null");}}

当然,如果你比较心细的话会发现 IDE 中会有警告,也可以很大程度上避免这个问题。

解法

不如换个“殊途同归”的办法,先用 HashMap 去实现同一个 key 的值覆盖的功能,然后通过 Collections.unmodifiableMap来实现不可编辑功能。

     Map<String, Integer> map = new HashMap<>();map.put("a", 1);map.put("b", 2);map.put("c", 3);map.put("d", 4);map.put("e", 5);map.put("f", null);Map<String, Integer> unmodifiableMap = Collections.unmodifiableMap(map);System.out.println(unmodifiableMap);

在这里插入图片描述

2.3 key 重复报错

复现

如果一不小心 key 重复,也会报 java.lang.IllegalArgumentException异常。

        ImmutableMap<String, Integer> build = ImmutableMap.<String, Integer>builder().put("a", 1).put("b", 2).put("c", 3).put("d",4).put("f",5).put("f",6).build();System.out.println(build);

在这里插入图片描述

源码

  /*** Returns an immutable map containing the given entries, in order.** @throws IllegalArgumentException if duplicate keys are provided*/public static <K, V> ImmutableMap<K, V> of(K k1, V v1, K k2, V v2) {return RegularImmutableMap.fromEntries(entryOf(k1, v1), entryOf(k2, v2));}

最底层会对 entry 进行校验:

  /*** Checks if the given key already appears in the hash chain starting at {@code keyBucketHead}. If* it does not, then null is returned. If it does, then if {@code throwIfDuplicateKeys} is true an* {@code IllegalArgumentException} is thrown, and otherwise the existing {@link Entry} is* returned.** @throws IllegalArgumentException if another entry in the bucket has the same key and {@code*     throwIfDuplicateKeys} is true* @throws BucketOverflowException if this bucket has too many entries, which may indicate a hash*     flooding attack*/@CanIgnoreReturnValuestatic <K, V> @Nullable ImmutableMapEntry<K, V> checkNoConflictInKeyBucket(Object key,Object newValue,@CheckForNull ImmutableMapEntry<K, V> keyBucketHead,boolean throwIfDuplicateKeys)throws BucketOverflowException {int bucketSize = 0;for (; keyBucketHead != null; keyBucketHead = keyBucketHead.getNextInKeyBucket()) {if (keyBucketHead.getKey().equals(key)) {if (throwIfDuplicateKeys) {checkNoConflict(/* safe= */ false, "key", keyBucketHead, key + "=" + newValue);} else {return keyBucketHead;}}if (++bucketSize > MAX_HASH_BUCKET_LENGTH) {throw new BucketOverflowException();}}return null;}

最终报错:

  static IllegalArgumentException conflictException(String conflictDescription, Object entry1, Object entry2) {return new IllegalArgumentException("Multiple entries with same " + conflictDescription + ": " + entry1 + " and " + entry2);}

解法

ImmutableMapbuilder除了提供 buid 之外, 在 31.0 版本之后还通过了 buildKeepingLastbuildOrThrow
在这里插入图片描述

可以通过 buildKeepingLast设置当 key 重复时取后面的值。

    /*** Returns a newly-created immutable map. The iteration order of the returned map is the order* in which entries were inserted into the builder, unless {@link #orderEntriesByValue} was* called, in which case entries are sorted by value.** <p>Prefer the equivalent method {@link #buildOrThrow()} to make it explicit that the method* will throw an exception if there are duplicate keys. The {@code build()} method will soon be* deprecated.** @throws IllegalArgumentException if duplicate keys were added*/public ImmutableMap<K, V> build() {return buildOrThrow();}/*** Returns a newly-created immutable map, or throws an exception if any key was added more than* once. The iteration order of the returned map is the order in which entries were inserted* into the builder, unless {@link #orderEntriesByValue} was called, in which case entries are* sorted by value.** @throws IllegalArgumentException if duplicate keys were added* @since 31.0*/public ImmutableMap<K, V> buildOrThrow() {return build(true);}/*** Returns a newly-created immutable map, using the last value for any key that was added more* than once. The iteration order of the returned map is the order in which entries were* inserted into the builder, unless {@link #orderEntriesByValue} was called, in which case* entries are sorted by value. If a key was added more than once, it appears in iteration order* based on the first time it was added, again unless {@link #orderEntriesByValue} was called.** <p>In the current implementation, all values associated with a given key are stored in the* {@code Builder} object, even though only one of them will be used in the built map. If there* can be many repeated keys, it may be more space-efficient to use a {@link* java.util.LinkedHashMap LinkedHashMap} and {@link ImmutableMap#copyOf(Map)} rather than* {@code ImmutableMap.Builder}.** @since 31.1*/public ImmutableMap<K, V> buildKeepingLast() {return build(false);}

低版本的话可以考虑先用 HashMap 构造数据,然后使用 com.google.common.collect.ImmutableMap#copyOf(java.util.Map<? extends K,? extends V>) 转换即可。

    Map<String, Integer> map = new HashMap<>();map.put("a", 1);map.put("b", 2);map.put("c", 3);map.put("d", 4);map.put("f", 5);map.put("f", 6);ImmutableMap<String, Integer> build = ImmutableMap.copyOf(map);System.out.println(build);

三、为什么?

3.1 为什么默认是 5 个键值对?

其实 31.0 版本,已经支持 10 个键值对了。
此处,斗胆猜测,of方法仅是为了提供更简单的构造 ImmutableMap的方法,而“通常” 5 个就足够了。
然而,实践中很多人发现 5 个并不够,因此高版本中支持 10个键值对。

Guava 也有相关 Issues 的讨论 ImmutableMap::of should accept more entries #2071
https://github.com/google/guava/issues/2071

image.png

3.2 为什么不允许键值为 null ?

Github 上也有相关讨论:
Question: Why RegularImmutableMap.fromEntryArray enforces “not null” policy on values? #5844

image.png

wiki 上有相关解释:
https://github.com/google/guava/wiki/UsingAndAvoidingNullExplained

使用 ChatGPT 对上述 wiki 进行关键信息提取:

在谷歌的 Guava 库的设计哲学中,不允许在 ImmutableMap(或其他类似的集合)中使用 null 值有几个关键原因:

防止错误:Guava 团队发现在 Google 的代码库中,大约 95% 的集合不应包含任何 null 值。允许 null 值会增加出错的风险,比如可能导致空指针异常。让这些集合在遇到 null 时快速失败(fail-fast)而不是默默接受 null,对开发者来说更有帮助。

消除歧义:null 值的含义通常不明确。例如,在使用 Map.get(key) 时,如果返回 null,可能是因为映射中该键对应的值为 null,或者该键在映射中不存在。这种歧义会导致理解和使用上的困难。

提倡更清晰的实践:在 Set 或 Map 中使用 null 值通常不是一个好的做法。更清晰的方法是在查找操作中显式处理 null,例如,如果你想在 Map 中使用 null 作为值,最好将那个条目留空,并保持一个单独的非空键集合。这样做可以避免混淆那些映射中键存在但值为 null,和那些映射中根本没有该键的情况。

选择适当的替代方案:如果你确实需要使用 null 值,并且遇到了不友好处理 null 的集合实现时,Guava 建议使用不同的实现。例如,如果 ImmutableList 不满足需求,可以使用 Collections.unmodifiableList(Lists.newArrayList()) 作为替代。

总体而言,Guava 库通过避免在其集合中使用 null,旨在提供更清晰、更健壮、且更易于维护的代码实践。

3.3 为什么重复 key 会报错?

我认为,主要是为了符合“不可变”的语义,既然是不可变,那么相同的 key 不应该重复放入到 map 中。其次,也可以避免意外的数据覆盖或丢失。

四、总结

虽然这个问题并不难,但很多人并不知道会有那么多“坑”,很多人都需要重复思考如何解决这些限制。
因此,本文总结在这里,希望对大家有帮助。


在这里插入图片描述

相关文章:

浅谈 Guava 中的 ImmutableMap.of 方法的坑

作者&#xff1a;明明如月学长&#xff0c; CSDN 博客专家&#xff0c;大厂高级 Java 工程师&#xff0c;《性能优化方法论》作者、《解锁大厂思维&#xff1a;剖析《阿里巴巴Java开发手册》》、《再学经典&#xff1a;《EffectiveJava》独家解析》专栏作者。 热门文章推荐&…...

Symbol()和迭代器生成器

目录 1、Symbol&#xff08;&#xff09; 2、迭代器生成器 执行流程 模拟生成器函数 for of 遍历迭代选择器 yield * Generator函数应用 1、Symbol&#xff08;&#xff09; Symbol表示独一无二的值 const s1 Symbol(a)const s2 Symbol(a)console.log(s1 s2) // fa…...

USB Type-C的基本原理

1 USB Type-C的基本原理 1.1 基本特性 Figure 1-1 USB Type-C接头外形 USB Type-C&#xff08;简称USB-C&#xff09;的基本特性&#xff1a; 1. 接口插座的尺寸与原来的Micro-USB规格一样小&#xff0c;约为8.3mm X 2.5mm 2. 可承受1万次反复插拔 3. 支持正反均可插入的“正反…...

HarmonyOS开发(八):动画及网络

1、动画概述 在ArkUI中&#xff0c;产生动画的方式是改变组件属性值并且指定相关的动画参数。当属性值发生变化后&#xff0c;按照动画参数&#xff0c;从原来的状态过渡到新的状态&#xff0c;就形成一个动画。 动画的相关参数如下&#xff1a; 属性名称 属性类型 默认值 …...

Pinctrl子系统和GPIO子系统

Pinctrl子系统&#xff1a; 借助Princtr子系统来设置一个Pin的复用和电气属性&#xff1b; pinctrl子系统主要做的工作是&#xff1a;1. 获取设备树中的PIN信息&#xff1b;2.根据获取到的pin信息来设置的Pin的复用功能&#xff1b;3.根据获取到的pin信息去设置pin的电气特性…...

Unittest单元测试框架之unittest构建测试套件

构建测试套件 在实际项目中&#xff0c;随着项目进度的开展&#xff0c;测试类会越来越多&#xff0c;可是直到现在我 们还只会一个一个的单独运行测试类&#xff0c;这在实际项目实践中肯定是不可行的&#xff0c;在 unittest中可以通过测试套件来解决该问题。 测试套件&…...

Django回顾4

一.过滤器 1.过滤器格式 {{变量|过滤器名字}} 2.怎么使用 1.注册app 2.在app下创建templatetags模块&#xff08;模块名只能是templatetags&#xff09; 3.在包下写一个py文件&#xff0c;随便命名 4.在py文件中写入&#xff1a;from django import template …...

Apache APISIX 体验指南

APISIX 体验指南 所有的 sh 脚本通过 git bash 执行。 出现错误仔细核对文档。 github 地址&#xff1a; 使用 docker 安装 apisix 确保本地安装 Docker 和 Docker-compose 如未安装参开以下文档安装&#xff1a; Docker&#xff1a;https://docs.docker.com/engine/install/c…...

Promise的resolve和reject方法(手写题)

1.resolve 2.reject 3.手写 1.resolve //构造函数上添加 resolve 方法 Promise.resolve function (value) {return new Promise((resolve, reject) > {if (value instanceof Promise) {value.then((val) > {resolve(val)},(err) > {reject(err)})} else {resolve(v…...

关于wiki的Unlink攻击理解--附例题BUUCTF-hitcontraining_bamboobox1

堆机制我研究了很久&#xff0c;一直没有什么很大的进展。堆相较于栈难度大的多。利用手法也多。目前还没有怎么做过堆题。这次就把理解了很久的Unlink写一写。然后找一题实践一下。 在glibc中&#xff0c;堆管理都是用一个个chunk去组织的。这个就不过多阐述。Unlink是glibc一…...

【linux】日志有哪些

Linux系统日志主要有以下几种类型&#xff1a; 内核及系统日志&#xff1a;这种日志数据由系统服务rsyslog统一管理&#xff0c;根据其主配置文件/etc/rsyslog.conf中设置决定内核消息及各种系统程序消息记录到什么位置。/var/log/message&#xff1a;该日志文件存放了内核消息…...

Redis主从复制实现RCE

文章目录 前置知识概念redis常用命令redis module 利用条件利用工具思路例题 [网鼎杯 2020 玄武组]SSRFMe总结 前置知识 概念 背景是多台服务器要保存同一份数据&#xff0c;如何实现其一致性呢&#xff1f;数据的读写操作是否每台服务器都可以处理&#xff1f;这里Redis就提供…...

Flutter应用程序的加固原理

在移动应用开发中&#xff0c;Flutter已经成为一种非常流行的技术选项&#xff0c;可以同时在Android和iOS平台上构建高性能、高质量的移动应用程序。但是&#xff0c;由于其跨平台特性&#xff0c;Flutter应用程序也面临着一些安全风险&#xff0c;例如反编译、代码泄露、数据…...

Centos7部署NFS

搭建NFS存储服务器--基于CentOS7系统 - jianmuzi - 博客园 在CentOS中搭建NFS - 陌上荼靡 - 博客园 NFS简介 NFS 是 Network FileSystem 的缩写&#xff0c;顾名思义就是网络文件存储系统&#xff0c;它最早是由 Sun 公司发展出来的&#xff0c;也是 FreeBSD 支持的文件系统…...

我已经开了一个融资融券的账户了,还可以再在别的券商开两融(信用账户)吗?

融资融券交易又称“证券信用交易”或保证金交易&#xff0c;是指投资者向具有融资融券业务资格的证券公司提供担保物&#xff0c;借入资金买入证券&#xff08;融资交易&#xff09;或借入证券并卖出&#xff08;融券交易&#xff09;的行为。 简单说就是融资做多&#xff0c;…...

Spring Cloud 版本升级记:OpenFeignClient与Gateway的爱恨交织

Spring Cloud 版本升级记&#xff1a;OpenFeignClient与Gateway的爱恨交织 近日&#xff0c;在负责的项目中&#xff0c;我对 Spring Boot、Spring Cloud 以及 Spring Cloud Alibaba 进行了版本升级。原以为会一切顺利&#xff0c;没想到却遭遇了 Spring Cloud Gateway 无法正…...

华为OD机试 - 最多购买宝石数目(Java JS Python C)

题目描述 橱窗里有一排宝石,不同的宝石对应不同的价格,宝石的价格标记为 gems[i] 0 ≤ i < nn = gems.length宝石可同时出售0个或多个,如果同时出售多个,则要求出售的宝石编号连续; 例如客户最大购买宝石个数为m,购买的宝石编号必须为:gems[i],gems[i+1],...,ge…...

【LeetCode】挑战100天 Day17(热题+面试经典150题)

【LeetCode】挑战100天 Day17&#xff08;热题面试经典150题&#xff09; 一、LeetCode介绍二、LeetCode 热题 HOT 100-192.1 题目2.2 题解 三、面试经典 150 题-193.1 题目3.2 题解 一、LeetCode介绍 LeetCode是一个在线编程网站&#xff0c;提供各种算法和数据结构的题目&…...

正则表达式的基本语法

1.正则表达式基本语法 两个特殊的符号^和$。他们的作用是分别指出一个字符串的开始和结束。例子如下&#xff1a; "^The"&#xff1a;表示所有以"The"开始的字符串&#xff08;"There"&#xff0c;"The cat"等&#xff09;&#xff1…...

使用visual Studio MFC 平台实现对灰度图添加椒盐噪声,并进行均值滤波与中值滤波

平滑处理–滤波 本文使用visual Studio MFC 平台实现对灰度图添加椒盐噪声&#xff0c;并进行均值滤波与中值滤波 关于其他MFC单文档工程可参考 01-Visual Studio 使用MFC 单文档工程绘制单一颜色直线和绘制渐变颜色的直线 02-visual Studio MFC 绘制单一颜色三角形、渐变颜色边…...

Django HMAC 请求签名校验与 Vue.js 实现安全通信

概要 在 Web 应用的开发过程中&#xff0c;确保数据传输的安全性和完整性是一个不容忽视的问题。使用 HMAC&#xff08;Hash-based Message Authentication Code&#xff09;算法对请求内容进行签名校验&#xff0c;是一种常见且有效的安全策略。本文将详细介绍如何在 Django …...

深度学习之循环神经网络

视频链接&#xff1a;6 循环神经网络_哔哩哔哩_bilibili 给神经网络增加记忆能力 对全连接层而言&#xff0c;输入输出的维数固定&#xff0c;因此无法处理序列信息 对卷积层而言&#xff0c;因为卷积核的参数是共享的&#xff0c;所以卷积操作与序列的长度无关。但是因为卷积…...

与原有视频会议系统对接

要实现与原有视频会议系统对接&#xff0c;需要确保通信协议的一致性。连通宝视频会议系统可与第三方视频会议系统对接。实现与第三方会议系统对接还可以使用会议室连接器&#xff0c;可以确保不同系统之间的数据传输和交互。 具体对接流程可能因不同品牌和类型的视频会议系统而…...

C# Serilog--可记录异常完整路径

1.Serilog安装 2.控制台代码 --设置日志记录器的最小级别为 Debug&#xff0c;即只记录 Debug 级别及以上的日志信息 --.WriteTo.File("logs\\log.txt", rollingInterval: RollingInterval.Day)&#xff1a;将日志信息写入到指定路径的文件中&#xff08;这里的路径…...

鉴源实验室 | 汽车网络安全攻击实例解析(三)

作者 | 张璇 上海控安可信软件创新研究院工控网络安全组 来源 | 鉴源实验室 社群 | 添加微信号“TICPShanghai”加入“上海控安51fusa安全社区” 引言&#xff1a;随着现代汽车技术的迅速发展&#xff0c;车辆的进入和启动方式经历了显著的演变。传统的物理钥匙逐渐被无钥匙进…...

php 中生成订单号

字母日期。。。。。。。 function setOrderNo($year 2011) {$yCode array(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z);$orderSn $yCode[intval(date(Y)) - $year] . strtoupper(dechex(date(m))) . date(d) . substr(time(), -5) . s…...

Jmeter工具+ant+jenkins实现持续集成

jmeterantjenkins持续集成 一、下载并配置jmeter 首先下载jmeter工具&#xff0c;并配置好环境变量&#xff1b;参考&#xff1a; jmeter默认保存的是.jtl格式的文件&#xff0c;要设置一下bin/jmeter.properties,文件内容&#xff0c;保存jmeter.save.saveservice.output_f…...

基于SSM的经典电影推荐网站设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…...

JavaScript中使用JSON的基本操作示例

简介 JSON&#xff08;JavaScript Object Notation&#xff09;是一种数据交换格式&#xff0c;也是JavaScript中处理数据的常见方式之一。JSON是一种轻量级的数据交换格式&#xff0c;易于阅读和编写&#xff0c;同时也易于解析和生成。在JavaScript中&#xff0c;可以使用内…...

上拉、下拉电阻的作用

上拉、下拉电阻的作用 (1)一般作为单键触发使用&#xff0c;如果芯片本身没有内接电阻&#xff0c;为了使单键维持在不被触发的状态或触发后回到原状态&#xff0c;必须在芯片外部接一个电阻&#xff0c;即保持芯片引脚高电平&#xff08;或低电平)输入&#xff0c;这样单击按键…...