跳到正文
Java 入门到面试指南
返回

05-选择填空判断题库

阶段:面试篇 | 题量:共 448 题(选择 307 + 填空 91 + 判断 50,含 85 道难题)| 用法:在应用「📝 选择填空练习」中在线作答(可按「📖 章节小考」下拉框逐章刷题、🎲 乱序防背题、🔁 只做错题),或直接阅读自测——遮住「答案」行,做完再对照「解析」,答错的知识点回到「详见」对应文件复习。

选择题

1.(Java基础·易)JDK、JRE、JVM 三者的包含关系是?

  • A. JDK ⊃ JRE ⊃ JVM
  • B. JVM ⊃ JRE ⊃ JDK
  • C. JRE ⊃ JDK ⊃ JVM
  • D. 三者互相独立

答案:A 解析:JDK = 开发工具 + JRE,JRE = JVM + 核心类库;只需运行程序装 JRE,要开发才装 JDK。详见《01-环境搭建与基础语法》

2.(Java基础·中)StringBuilder sb = new StringBuilder("a"); 调用 change(sb),方法体为 void change(StringBuilder x){ x.append("b"); x = new StringBuilder("c"); },之后 sb 的内容是?

  • A. a
  • B. c
  • C. ab
  • D. bc

答案:C 解析:Java 只有值传递:append 修改的是同一个对象,而重新赋值只改变了形参的指向,不影响实参。详见《01-环境搭建与基础语法》

3.(Java基础·易)Integer a = 127, b = 127; System.out.println(a == b); 输出什么?

  • A. false
  • B. 编译错误
  • C. 不确定
  • D. true

答案:D 解析:IntegerCache 缓存 -128~127,范围内 == 比较的是同一个缓存对象;超出范围(如 128)必须用 equals。详见《02-面向对象与核心类》

4.(Java基础·易)以下哪个不属于面向对象三大特性?

  • A. 重载
  • B. 封装
  • C. 继承
  • D. 多态

答案:A 解析:三大特性是封装、继承、多态;重载是编译期确定同签名不同参数的方法,不属于三大特性。详见《02-面向对象与核心类》

5.(Java基础·易)try-with-resources 的主要作用是?

  • A. 自动提交事务
  • B. 自动关闭实现了 AutoCloseable 的资源
  • C. 捕获所有异常
  • D. 提升运行速度

答案:B 解析:编译器会生成 finally 中的关闭逻辑,避免资源泄漏,优于手写 try-catch-finally。详见《04-异常-IO-反射-注解》

6.(Java基础·中)关于 String 不可变,正确的解释是?

  • A. String 是可变的,只是习惯上不改
  • B. 频繁拼接字符串应优先用 String
  • C. String 类被 final 修饰、内部数据私有且不暴露修改方法
  • D. String 是基本数据类型

答案:C 解析:不可变带来安全性、字符串常量池复用、hashCode 可缓存;频繁拼接用 StringBuilder。详见《02-面向对象与核心类》

7.(集合·中)JDK 1.8 中 ArrayList 首次 add 时容量扩到 10,此后每次扩容为原来的?

  • A. 2 倍
  • B. 1.2 倍
  • C. 3 倍
  • D. 1.5 倍

答案:D 解析:newCapacity = oldCapacity + (oldCapacity >> 1),即 1.5 倍向下取整。详见《03-集合与泛型》

8.(集合·中)HashMap(JDK 1.8)中链表转红黑树的条件是?

  • A. 链表长度 ≥ 8 且数组长度 ≥ 64
  • B. 链表长度 ≥ 8 即可
  • C. 链表长度 ≥ 16
  • D. 元素总数 ≥ 64

答案:A 解析:数组长度不足 64 时优先扩容而不是树化;红黑树退化回链表的阈值是 6。详见《03-集合与泛型》

9.(集合·易)HashSet 判断元素是否重复依赖的方法是?

  • A. 仅 equals
  • B. hashCode 与 equals
  • C. 仅 ==
  • D. compareTo

答案:B 解析:先用 hashCode 定位桶,再在桶内用 equals 精确比较,两者必须一起重写。详见《03-集合与泛型》

10.(集合·易)下列集合中线程安全的是?

  • A. ArrayList
  • B. HashMap
  • C. Vector
  • D. LinkedList

答案:C 解析:Vector 方法用 synchronized 修饰;现代并发场景更推荐 ConcurrentHashMap 或 CopyOnWriteArrayList。详见《03-集合与泛型》

11.(集合·易)用 for-each 遍历 ArrayList 时直接调用 list.remove(o),通常会?

  • A. 正常删除
  • B. 编译错误
  • C. 静默删除所有元素
  • D. 抛出 ConcurrentModificationException

答案:D 解析:fail-fast 机制检测到 modCount 被修改即抛异常;正确做法是 Iterator.remove() 或 removeIf。详见《03-集合与泛型》

12.(并发·易)直接调用线程对象的 run() 而不是 start(),结果是?

  • A. 在当前线程中作为普通方法执行
  • B. 创建新线程执行
  • C. 抛出异常
  • D. 永久阻塞

答案:A 解析:只有 start() 会让 JVM 创建新线程并调用 run();直接调 run() 与普通方法调用无异。详见《01-多线程与并发编程》

13.(并发·易)volatile 能保证的是?

  • A. 原子性
  • B. 可见性与有序性
  • C. 原子性与可见性
  • D. 原子性、可见性、有序性都保证

答案:B 解析:volatile 保证可见性和禁止指令重排,但 i++ 这类复合操作仍需 synchronized 或原子类。详见《01-多线程与并发编程》

14.(并发·中)线程池提交任务后的处理顺序是?

  • A. 直接创建线程直到最大线程数
  • B. 核心线程 → 阻塞队列 → 非核心线程 → 拒绝策略
  • C. 先入队列再创建核心线程
  • D. 顺序随机

答案:B 解析:先占满核心线程,队列满了才扩非核心线程,到 max 后触发拒绝策略——这也是 FixedThreadPool 无界队列堆积 OOM 的根源。详见《01-多线程与并发编程》

15.(并发·中)synchronized 锁升级的正确顺序是?

  • A. 重量级锁 → 轻量级锁 → 偏向锁
  • B. 轻量级锁 → 偏向锁 → 重量级锁
  • C. 偏向锁 → 轻量级锁 → 重量级锁
  • D. 不存在锁升级

答案:C 解析:JVM 按竞争激烈程度逐步升级且不可逆,目的是减少无竞争场景的开销。详见《01-多线程与并发编程》

16.(并发·中)ThreadLocal 可能内存泄漏的根本原因是?

  • A. key 是强引用
  • B. value 是弱引用
  • C. 底层是 ArrayList
  • D. key 是弱引用但 value 是强引用,线程长期存活时 value 无法回收

答案:D 解析:线程池中线程长期存活,ThreadLocalMap 的 value 无法自动回收,用完必须调用 remove()。详见《01-多线程与并发编程》

17.(并发·易)以下哪个不是 ThreadPoolExecutor 的拒绝策略?

  • A. FairPolicy
  • B. AbortPolicy
  • C. CallerRunsPolicy
  • D. DiscardPolicy

答案:A 解析:四种拒绝策略是 AbortPolicy、CallerRunsPolicy、DiscardPolicy、DiscardOldestPolicy。详见《01-多线程与并发编程》

18.(JVM·中)以下运行时区域中,规范规定不会抛出 OutOfMemoryError 的是?

  • A. 堆
  • B. 程序计数器
  • C. 虚拟机栈
  • D. 元空间

答案:B 解析:程序计数器是唯一没有规定任何 OOM 情况的区域;栈是 StackOverflowError,堆和元空间都会 OOM。详见《02-JVM核心》

19.(JVM·易)判断对象是否可回收,主流商用虚拟机采用?

  • A. 引用计数法
  • B. 随机回收
  • C. 可达性分析(从 GC Roots 出发)
  • D. 手动释放

答案:C 解析:可达性分析解决了引用计数法的循环引用问题;GC Roots 包括栈帧局部变量、静态变量、常量、JNI 引用等。详见《02-JVM核心》

20.(JVM·中)双亲委派机制的主要作用是?

  • A. 加快类加载速度
  • B. 是节省内存的唯一手段
  • C. 为了支持热更新
  • D. 避免核心类被篡改与类的重复加载

答案:D 解析:子加载器先委托父加载器,保证 String 等核心类始终由 Bootstrap 加载,安全且唯一。详见《02-JVM核心》

21.(JVM·中)关于 G1 收集器,下列说法正确的是?

  • A. 将堆划分为多个 Region,可设定目标停顿时间
  • B. 只能回收新生代
  • C. 基于标记-清除,必然产生大量碎片
  • D. 是 JDK 8 的默认收集器

答案:A 解析:G1 按 Region 管理、可预测停顿(-XX:MaxGCPauseMillis),整体标记-整理、Region 间复制,JDK 9+ 默认。详见《02-JVM核心》

22.(MySQL·易)InnoDB 的默认事务隔离级别是?

  • A. 读未提交
  • B. 可重复读
  • C. 读已提交
  • D. 串行化

答案:B 解析:RR 通过 MVCC + 临键锁在很大程度上避免幻读;互联网公司常改用 RC 以减少锁冲突。详见《索引-事务-锁与调优》

23.(MySQL·中)假设 name 列有索引,下列条件能走索引的是?

  • A. name LIKE ‘%三’
  • B. LEFT(name,1) = ‘张’
  • C. name LIKE ‘张%’
  • D. name + 1 = ‘张三’

答案:C 解析:左模糊、对索引列使用函数或运算都会导致索引失效,只有右前缀模糊可以走索引。详见《索引-事务-锁与调优》

24.(MySQL·易)事务 ACID 中的 I 指的是?

  • A. 原子性
  • B. 一致性
  • C. 持久性
  • D. 隔离性

答案:D 解析:Atomicity 原子性、Consistency 一致性、Isolation 隔离性、Durability 持久性。详见《索引-事务-锁与调优》

25.(MySQL·中)binlog(二进制日志)的主要用途是?

  • A. 主从复制与时间点恢复
  • B. 崩溃后恢复未完成的事务
  • C. 保证事务原子性
  • D. 记录慢查询

答案:A 解析:崩溃恢复靠 redo log;binlog 是 Server 层日志,用于主从复制和数据归档恢复。详见《索引-事务-锁与调优》

26.(MySQL·易)InnoDB 聚簇索引的叶子节点存放的是?

  • A. 主键值
  • B. 整行数据
  • C. 索引列 + 主键
  • D. 数据页地址

答案:B 解析:聚簇索引即主键索引,叶子存整行;二级索引叶子存”索引列+主键”,查非覆盖列需回表。详见《索引-事务-锁与调优》

27.(MySQL·易)varchar(50) 在 utf8mb4 字符集下的含义是?

  • A. 最多 50 字节
  • B. 固定占 50 字节
  • C. 最多 50 个字符
  • D. 最多存 50 个汉字

答案:C 解析:varchar 按字符计长度,实际占用字节数取决于字符集与内容。详见《MySQL基础与SQL实战》

28.(Redis·中)Redis 6.0 引入多线程的目的是?

  • A. 让命令执行并行化
  • B. 彻底替换单线程模型
  • C. 为了实现分布式锁
  • D. 处理网络 IO 与协议解析,命令执行仍是单线程

答案:D 解析:Redis 瓶颈常在网络 IO;引入多线程读写网络,命令执行保持单线程避免锁竞争。详见《01-Redis》

29.(Redis·易)解决缓存穿透的常用手段是?

  • A. 缓存空值与布隆过滤器
  • B. 过期时间加随机值
  • C. 互斥锁重建缓存
  • D. 多级缓存

答案:A 解析:穿透是查询根本不存在的数据;B 针对 AOF 雪崩、C 针对击穿、D 用于高并发兜底。详见《01-Redis》

30.(Redis·中)关于 RDB 持久化,正确的是?

  • A. 每条写命令都实时落盘
  • B. 某一时刻的内存快照,恢复快但可能丢失最后一次快照后的数据
  • C. 生成可读的文本命令日志
  • D. 不能与 AOF 同时开启

答案:B 解析:AOF 才是命令日志;RDB 二进制紧凑恢复快,Redis 4.0+ 支持 RDB+AOF 混合持久化。详见《01-Redis》

31.(Redis·易)大量 key 同一时刻过期导致数据库瞬间被压垮,这属于?

  • A. 缓存穿透
  • B. 缓存击穿
  • C. 缓存雪崩
  • D. 缓存污染

答案:C 解析:雪崩=大规模 key 同时失效或 Redis 宕机;过期时间加随机值、多级缓存、集群高可用是常用方案。详见《01-Redis》

32.(Redis·易)实现排行榜功能最合适的 Redis 数据结构是?

  • A. String
  • B. Hash
  • C. Set
  • D. ZSet

答案:D 解析:ZSet 按 score 排序,天然支持 ZINCRBY 加分、ZRANGE/ZREVRANGE 取 TopN。详见《01-Redis》

33.(Redis·中)Cache Aside 模式推荐的更新顺序是?

  • A. 先更新数据库,再删除缓存
  • B. 先更新缓存,再更新数据库
  • C. 先删除缓存,再更新数据库,无兜底
  • D. 缓存与数据库同时双写

答案:A 解析:先更库再删缓存的不一致窗口最小;并发读写场景可配合延迟双删、过期时间兜底。详见《01-Redis》

34.(消息队列·易)消息队列解决的核心问题是?

  • A. 数据持久存储
  • B. 异步、解耦、削峰
  • C. 数据计算
  • D. 服务发现

答案:B 解析:典型场景:异步通知(注册发短信)、系统解耦(订单与库存)、流量削峰(秒杀)。详见《02-RabbitMQ》

35.(消息队列·中)关于 Kafka 的消息顺序,正确的是?

  • A. Topic 内天然全局有序
  • B. 消费者越多越有序
  • C. 单分区内有序,按 key 路由可保证同类消息有序
  • D. 顺序与分区无关

答案:C 解析:顺序只保证在分区内;需要顺序的消息按业务 key 路由到同一分区,且该分区在消费组内只由一个消费者处理。详见《03-Kafka》

36.(消息队列·中)以下哪项不是 Kafka 高吞吐的原因?

  • A. 顺序写磁盘
  • B. 零拷贝(sendfile)
  • C. 批量发送与压缩
  • D. 每条消息立即 fsync 刷盘

答案:D 解析:Kafka 依赖页缓存批量刷盘,逐条 fsync 反而严重拖慢写入。详见《03-Kafka》

37.(消息队列·中)RocketMQ 事务消息的正确流程是?

  • A. 发送半消息 → 执行本地事务 → 提交/回滚,配合事务回查兜底
  • B. 先投递消息再执行本地事务,失败无法处理
  • C. 由消费者代替生产者执行本地事务
  • D. 依赖数据库两阶段提交

答案:A 解析:半消息对消费者不可见;生产者宕机时 Broker 通过回查确认本地事务状态,实现最终一致。详见《04-RocketMQ》

38.(消息队列·易)防止重复消费污染数据的通用思路是?

  • A. 要求 MQ 绝对不重复
  • B. 消费端幂等:唯一业务 ID + 去重表/状态机
  • C. 把消费模式改为只消费一次
  • D. 无需处理

答案:B 解析:至少一次投递下重复不可避免,幂等是标准答案:数据库唯一键、Redis setnx 去重等。详见《02-RabbitMQ》

39.(框架·中)同类中方法 A 直接调用带 @Transactional 的方法 B,事务失效的原因是?

  • A. 事务传播行为冲突
  • B. 方法 B 不是 public
  • C. 自调用走的是原始对象而非代理对象
  • D. 数据库不支持事务

答案:C 解析:Spring 事务基于 AOP 代理,自调用绕过了代理对象;可拆分类、注入自身代理或用 AopContext 解决。注意 C 也可能失效,但”自调用”是本题场景主因。详见《Spring核心》

40.(框架·易)Spring Bean 的默认作用域是?

  • A. prototype
  • B. request
  • C. session
  • D. singleton

答案:D 解析:默认单例;有状态Bean可用 @Scope(“prototype”)。详见《Spring核心》

41.(框架·易)Spring AOP 底层实现采用的是?

  • A. 动态代理(JDK 动态代理或 CGLIB)
  • B. 反射工具类
  • C. 手写字节码注入
  • D. 操作系统信号量

答案:A 解析:目标类有接口时默认 JDK 动态代理,无接口时用 CGLIB 子类代理。详见《Spring核心》

42.(框架·中)SpringBoot 自动配置的加载逻辑是?

  • A. 扫描并加载所有第三方类
  • B. 读取 AutoConfiguration.imports 列出的自动配置类,再按 @ConditionalOnXxx 条件筛选生效
  • C. 用户必须手动注册所有 Bean
  • D. 启动时随机加载部分配置

答案:B 解析:@EnableAutoConfiguration 聚合加载配置清单,条件注解(如类路径、已有 Bean)决定是否生效。详见《SpringBoot实战》

43.(框架·易)MyBatis 中 #{} 与 ${} 的区别,正确的是?

  • A. 二者完全等价
  • B. ${} 更安全
  • C. #{} 是预编译占位符可防 SQL 注入,${} 是字符串拼接有注入风险
  • D. #{} 不能用在任何地方

答案:C 解析:动态表名、ORDER BY 列名等无法参数化的位置才用 ${},且必须白名单校验。详见《MyBatis》

44.(框架·易)MyBatis 一级缓存的作用范围是?

  • A. 单个 Mapper 接口
  • B. 应用全局
  • C. 单个线程
  • D. SqlSession

答案:D 解析:同一 SqlSession 内相同查询命中一级缓存(默认开启);二级缓存跨会话,分布式下易脏读应慎用。详见《MyBatis》

45.(框架·难)Spring 三级缓存能解决的循环依赖是?

  • A. singleton 作用域 + setter/字段注入的循环依赖
  • B. 所有循环依赖
  • C. 构造器注入的循环依赖
  • D. prototype 作用域的循环依赖

答案:A 解析:三级缓存靠”提前暴露早期引用”;构造器注入在实例化阶段就需要依赖,prototype 每次新建,都无法用三级缓存解决。详见《Spring核心》

46.(分布式与高并发·中)关于 CAP 理论,实践中更准确的理解是?

  • A. 三个特性可以同时任选
  • B. 分区不可避免,实际是在 CP 与 AP 之间取舍
  • C. 应该追求 CA 而放弃 P
  • D. P 可以通过配置关闭

答案:B 解析:网络分区必然发生,P 是必选项;无分区时才能同时兼顾 C 与 A,如 ZK 偏 CP、Eureka 偏 AP。详见《01-分布式理论基础》

47.(分布式与高并发·中)雪花算法的结构中不包含以下哪部分?

  • A. 符号位
  • B. 时间戳
  • C. 随机数
  • D. 机器 ID

答案:C 解析:标准结构是 1 位符号 + 41 位时间戳 + 10 位机器 ID + 12 位序列号;时钟回拨会产生重复 ID,需要兜底策略。详见《01-分布式理论基础》

48.(分布式与高并发·中)与漏桶算法相比,令牌桶的特点是?

  • A. 恒定速率处理请求
  • B. 不能用于限流
  • C. 只能单机使用
  • D. 允许一定程度的突发流量

答案:D 解析:令牌桶积攒令牌允许突发消费;漏桶恒定速率流出,平滑但无突发能力。详见《02-高并发系统设计》

49.(Java8·易)以下哪一项不是 Java 8 引入的新特性?

  • A. sealed 密封类
  • B. Lambda 表达式与函数式接口
  • C. Stream API
  • D. Optional

答案:A 解析:sealed 是 Java 17 的特性;Java 8 的代表性新特性是 Lambda、函数式接口、Stream、Optional、接口默认方法与新日期 API。详见《05-Lambda-Stream与Java8新特性》

50.(Java8·中)关于 Stream 的 map 与 flatMap,正确的是?

  • A. 二者完全等价
  • B. map 将每个元素映射为另一个元素,flatMap 将每个元素映射为流后再拼接扁平化
  • C. flatMap 只能用于数值流
  • D. map 会自动去重

答案:B 解析:flatMap 常用于「一对多展开」,例如把句子流拆成单词流。详见《05-Lambda-Stream与Java8新特性》

51.(Java8·易)Optional 的设计目的是?

  • A. 彻底取代所有 null 判断
  • B. 提升程序运行性能
  • C. 显式表达”可能为空”,用链式 API 减少 NPE
  • D. 替代异常处理

答案:C 解析:主要用于方法返回值;不建议用作字段与入参,也不建议直接 get()。详见《05-Lambda-Stream与Java8新特性》

52.(网络·易)TCP 建立连接需要进行几次握手?

  • A. 2 次
  • B. 4 次
  • C. 1 次
  • D. 3 次

答案:D 解析:三次握手确认双方收发能力;两次无法确认”客户端能收到”,四次则冗余。详见《03-IO模型与网络编程》

53.(网络·中)NIO 的核心三组件是?

  • A. Channel、Buffer、Selector
  • B. Stream、Reader、Writer
  • C. Socket、ServerSocket、URL
  • D. Thread、Runnable、Future

答案:A 解析:一个 Selector 可监听多个 Channel 的事件,单线程管理大量连接,即 IO 多路复用。详见《03-IO模型与网络编程》

54.(网络·中)以下哪项不是 Netty 高性能的原因?

  • A. 主从 Reactor 线程模型
  • B. 每个连接独占一个线程
  • C. 零拷贝与池化 Buffer
  • D. eventLoop 串行无锁化设计

答案:B 解析:「每连接一线程」正是 BIO 的模型,连接多时线程爆炸;Netty 用少量 eventLoop 线程服务大量连接。详见《03-IO模型与网络编程》

55.(网络·易)与 TCP 相比,UDP 的特点是?

  • A. 面向连接、可靠传输
  • B. 必须先握手才能发数据
  • C. 无连接、不保证可靠但开销小、延迟低
  • D. 自带拥塞控制

答案:C 解析:适合音视频、DNS、游戏等实时性优先、可容忍少量丢包的场景。详见《03-IO模型与网络编程》

56.(框架·中)微服务架构中,注册中心的作用是?

  • A. 存储业务数据
  • B. 代替数据库做缓存
  • C. 部署应用
  • D. 服务提供者注册地址,消费者动态发现可用实例

答案:D 解析:配合心跳做健康检查,实例上下线自动感知,调用方从注册中心拉取可用地址列表。详见《SpringCloud微服务》

57.(框架·易)OpenFeign 实现声明式 HTTP 调用的底层原理是?

  • A. 为接口生成动态代理,把方法调用翻译成 HTTP 请求
  • B. 反射调用 Controller
  • C. 需要手写 OkHttp 代码
  • D. 依赖数据库存储过程

答案:A 解析:@FeignClient 接口在启动时生成代理对象,结合负载均衡选择实例发起请求。详见《SpringCloud微服务》

58.(框架·中)下游服务大量超时,调用方暂时切断对它的请求并直接走兜底逻辑,这属于?

  • A. 限流
  • B. 熔断
  • C. 负载均衡
  • D. 灰度发布

答案:B 解析:熔断像电闸:错误率或慢调用超阈值后跳闸快速失败,防止雪崩,之后半开状态试探恢复。详见《SpringCloud微服务》

59.(Elasticsearch·易)Elasticsearch 实现全文检索的核心数据结构是?

  • A. B+ 树
  • B. 哈希表
  • C. 倒排索引
  • D. 跳表

答案:C 解析:文本分词后建立「词项 → 文档列表」的映射,查询时先查词再取文档,天然适合全文搜索。详见《05-Elasticsearch》

60.(Elasticsearch·中)关于 keyword 与 text 类型,正确的是?

  • A. 二者都会分词
  • B. keyword 会分词,text 不分词
  • C. 都不能用于聚合
  • D. text 会分词用于全文检索,keyword 不分词、适合精确匹配与排序聚合

答案:D 解析:状态码、手机号等用 keyword 精确匹配;商品描述等长文本用 text。详见《05-Elasticsearch》

61.(Nginx·易)以下哪项不是 Nginx 的典型用途?

  • A. 消息存储转发
  • B. 反向代理
  • C. 负载均衡
  • D. 静态资源服务

答案:A 解析:消息存储转发是消息队列的职责;Nginx 基于 epoll,擅长高并发连接接入与转发。详见《07-Nginx》

62.(Nginx·中)upstream 中配置 ip_hash 的作用是?

  • A. 随机分发请求
  • B. 同一客户端 IP 固定转发到同一后端节点
  • C. 按权重分发
  • D. 执行健康检查

答案:B 解析:实现会话保持(不依赖 Cookie);缺点是后端扩缩容时分布会变化。详见《07-Nginx》

63.(MongoDB·易)MongoDB 的数据模型是?

  • A. 二维关系表
  • B. 图结构
  • C. 文档(BSON)
  • D. 纯键值对

答案:C 解析:文档模型 schema 灵活、嵌套结构免多表关联;概念对应:库-集合-文档 ≈ 库-表-行。详见《08-MongoDB》

64.(MongoDB·中)以下哪种场景最适合 MongoDB?

  • A. 银行核心账务(强事务)
  • B. 复杂的多表 JOIN 报表
  • C. 计费扣款
  • D. 商品详情这类字段灵活、结构多变的内容管理

答案:D 解析:MongoDB 的弱项正是强事务与复杂关联;内容管理、日志埋点、爬虫数据是它的强项。详见《08-MongoDB》

65.(Java基础·中)Integer x = 128, y = 128; 执行 System.out.println(x == y); 输出什么?

  • A. false
  • B. true
  • C. 编译错误
  • D. 抛出 NullPointerException

答案:A 解析:IntegerCache 只缓存 -128~127,128 会各自新建对象,== 比地址为 false;比较值必须用 equals。详见《02-面向对象与核心类》

66.(Java基础·中)final StringBuilder sb = new StringBuilder("a"); sb.append("b"); sb = new StringBuilder("c"); 的结果是?

  • A. 正常运行,sb 内容为 abc
  • B. 编译错误:不能对 final 变量重新赋值
  • C. 正常运行,sb 内容为 c
  • D. 运行时抛出 IllegalStateException

答案:B 解析:final 只禁止引用再次指向别处,指向的对象内容仍可修改;append 合法,第二次赋值非法。详见《02-面向对象与核心类》

67.(Java基础·中)方法体为 try { return 1; } finally { return 2; },调用方拿到的返回值是?

  • A. 1
  • B. 编译错误:finally 中不允许 return
  • C. 2
  • D. 抛出异常

答案:C 解析:finally 在 return 之前执行,其中的 return 会覆盖 try 的返回值并吞掉异常;这是典型的坑,实际编码禁止在 finally 写 return。详见《04-异常-IO-反射-注解》

68.(集合·中)某类只重写了 equals 没有重写 hashCode,用它作 HashMap 的 key,put(k,v) 后用另一个字段相等的新对象 get(k2),通常得到?

  • A. v
  • B. 编译错误
  • C. 抛出 ClassCastException
  • D. null

答案:D 解析:HashMap 先用 hashCode 定位桶,未重写 hashCode 时两个对象哈希不同、落到不同桶,equals 根本没机会被调用。详见《03-集合与泛型》

69.(集合·中)List<Integer> list = Arrays.asList(1, 2,3); 之后执行 list.set(0, 9) 和 list.add(4),结果是?

  • A. set 成功,add 抛出 UnsupportedOperationException
  • B. set 和 add 都成功
  • C. set 抛异常,add 成功
  • D. 两者都编译错误

答案:A 解析:Arrays.asList 返回的是固定大小的视图(底层是数组),支持改元素 set,不支持改变长度的 add/remove。详见《03-集合与泛型》

70.(并发·中)线程池 corePoolSize=1、maximumPoolSize=2、队列容量=1,连续提交 4 个耗时任务,会观察到?

  • A. 1 个线程执行,其余排队
  • B. 2 个线程执行,1 个在队列中,第 4 个触发拒绝策略
  • C. 4 个线程并行执行
  • D. 4 个任务全部被拒绝

答案:B 解析:任务 1 建核心线程,任务 2 入队,任务 3 因队列已满才建非核心线程,任务 4 已达 max 走拒绝策略——队列先于非核心线程。详见《01-多线程与并发编程》

71.(并发·中)多个线程共用同一个 SimpleDateFormat 实例做 parse/format,最典型的现象是?

  • A. 完全正常,只是慢一点
  • B. 编译错误,SimpleDateFormat 不可共享
  • C. 结果错乱或抛出解析异常
  • D. 自动加锁串行执行

答案:C 解析:SimpleDateFormat 内部持有可变 Calendar,非线程安全;应改用线程安全的 DateTimeFormatter,或每次新建、用 ThreadLocal 持有。详见《05-Lambda-Stream与Java8新特性》

72.(Java基础·中)System.out.println(0.1 + 0.2 == 0.3); 输出什么?

  • A. true
  • B. 编译错误
  • C. 不确定,取决于 JVM 版本
  • D. false

答案:D 解析:0.1/0.2 的二进制浮点表示无法精确存储,相加为 0.30000000000000004;金额计算用 BigDecimal 或 long 存分。详见《01-环境搭建与基础语法》

73.(并发·难)AQS 中的 state 变量,在独占模式与共享模式下分别表示什么?

  • A. 独占模式下表示资源占用/重入次数,共享模式下表示剩余可用许可数
  • B. 两种模式下都表示持有者的线程编号
  • C. 两种模式下都表示等待队列的长度
  • D. 由开发者随意定义,AQS 内部并不使用它

答案:A 解析:ReentrantLock 用 state 记重入次数,Semaphore/CountDownLatch 用 state 记许可数/计数,tryAcquire 与 tryAcquireShared 语义由此不同。详见《01-多线程与并发编程》

74.(并发·难)ThreadLocal 泄漏链路中,当外部 strong 引用被置 null 后,正确的描述是?

  • A. key 被回收,value 同时被自动回收
  • B. key 被回收变成 null,但 value 仍被 Thread→ThreadLocalMap→Entry 强引用链持有
  • C. key 和 value 都不会被回收,因为 Entry 是强引用
  • D. value 是弱引用,随时可能被 GC 回收

答案:B 解析:Entry 继承 WeakReference,key 弱、value 强。线程不死则 value 无法回收,get/set 时的惰性清理不可靠,所以用完必须 remove()。详见《01-多线程与并发编程》

75.(JVM·难)并发标记出现”漏标”(对象明明活着却被回收)需要同时满足两个条件,是?

  • A. 白色对象被两个黑色对象同时引用
  • B. 新生代对象被老年代引用
  • C. 黑色对象新增了指向白色对象的引用,且灰色到该白色对象的路径被删除
  • D. 标记线程与应用线程同时运行

答案:C 解析:三色标记只破坏一个条件不会漏标;CMS 用增量更新(写屏障记录黑→白新引用)、G1 用 SATB 快照(记录删除前的引用)分别解决。详见《02-JVM核心》

76.(JVM·难)双亲委派机制被”破坏”的典型场景不包括以下哪项?

  • A. JDBC 通过 SPI 让 DriverManager 反向调用 AppClassLoader 加载 Driver
  • B. Tomcat 各 Webapp 的类加载器优先加载自己的类,实现应用隔离
  • C. 热部署/OSGi 用自定义 ClassLoader 替换旧类实现动态更新
  • D. 用 Class.forName() 显式指定类名加载一个类

答案:D 解析:Class.forName 仍走调用者所在类加载器并向上委托,没有破坏委派;A 靠线程上下文类加载器,B、C 是打破委派顺序的真实需求。详见《02-JVM核心》

77.(MySQL·难)RR 隔离级别下,执行 UPDATE t SET x=1 WHERE name='a' 而 name 上没有索引,InnoDB 的加锁行为是?

  • A. 扫描过程中对所有行都加行锁(约等于锁全表),无匹配也不释放已扫描行
  • B. 只对匹配的行加锁
  • C. 只加表级元数据锁,不加行锁
  • D. 直接抛出”无索引不能更新”的错误

答案:A 解析:InnoDB 锁的是”扫过的记录”,无索引即全表扫描,于是每行都被加锁;这也解释了为什么 UPDATE/DELETE 条件列必须有索引。详见《索引-事务-锁与调优》

78.(MySQL·难)关于 MVCC 的 ReadView 生成时机,正确的是?

  • A. RR 和 RC 都在事务开始时生成
  • B. RR 在事务第一次快照读时生成并整个事务复用;RC 每次快照读都重新生成
  • C. RR 每次 select 都重新生成,所以能读到最新数据
  • D. ReadView 只在加锁读时生成,快照读不需要

答案:B 解析:这正是”可重复读”与”读已提交”差异的实现根源:RR 复用一份快照,RC 每次重新取,因而能看到别的事务已提交的更新。详见《索引-事务-锁与调优》

79.(分布式与高并发·难)关于 Redlock 算法的主要争议,下列说法最准确的是?

  • A. 它的争议在于只能部署在单机 Redis 上
  • B. 争议在于它必须依赖 ZooKeeper 才能工作
  • C. 在 GC 停顿或时钟漂移下,客户端可能在锁已过期后仍以为持有锁,需要 fencing token 兜底
  • D. 学界已公认它完全不可用

答案:C 解析:Kleppmann 指出:进程暂停跨越租期后依旧会写,多节点也无法阻止;antirez 则反驳假设过于悲观。实践中单点故障容忍度高的业务多用单实例锁 + 幂等。详见《01-分布式理论基础》

80.(并发·难)多个线程竞争同一把锁时,锁升级各阶段的触发条件正确的是?

  • A. 只要有两个线程访问就直接重量级锁
  • B. 轻量级锁竞争失败会退回偏向锁
  • C. 偏向锁和轻量级锁是同时获取的
  • D. 只有一个线程访问时保持偏向,出现第二个线程竞争升级为轻量级,自旋失败/竞争加剧才膨胀为重量级

答案:D 解析:升级路径单向不可逆,代价依次升高:偏向只需一次 CAS 记录线程 ID,轻量级靠 CAS+自旋,重量级依赖 OS Mutex 挂起线程。详见《01-多线程与并发编程》

81.(消息队列·难)Kafka 消费组频繁发生 Rebalance(Rebalance 风暴)的常见诱因是?

  • A. 消费者处理时间超过 max.poll.interval.ms 被判定失效,反复进出组触发全员重分配
  • B. 分区数少于消费者数量
  • C. Broker 端开启了事务
  • D. 生产者 acks 配置为 all

答案:A 解析:Rebalance 是 stop-the-world 的,期间整组停止消费;调大单次处理量上限、缩小批处理、优化耗时才是正解,分区数不足只会造成空闲。详见《03-Kafka》

82.(JVM·难)安全点(Safepoint)的准确含义是?

  • A. 内存不足时触发 Full GC 的时刻
  • B. 线程执行到指定位置后暂停,此时所有线程的栈与引用关系可被精确定位
  • C. 每次对象分配前都要检查的位置
  • D. 只有方法调用才是安全点,循环不是

答案:B 解析:GC、偏向锁撤销、线程 dump 等操作要求线程在安全点停顿,靠 OopMap 定位引用;长循环需插入轮询指令,否则 safepoint 停顿时间被拉长。详见《02-JVM核心》

83.(MySQL·难)以下哪个 SELECT 一定需要回表(假设只有 name 单列索引)?

  • A. SELECT id FROM t WHERE name = ‘a’
  • B. SELECT name FROM t WHERE name = ‘a’
  • C. SELECT age FROM t WHERE name = ‘a’
  • D. SELECT count(*) FROM t WHERE name = ‘a’

答案:C 解析:二级索引叶子只存索引列与主键,age 不在索引中只能拿主键回聚簇索引取整行;A、B、D 需要的列都在索引里,属于覆盖索引。详见《索引-事务-锁与调优》

84.(操作系统·中)fork() 之后父子进程初始时的内存状态是?

  • A. 父进程内存被完整复制到子进程的物理内存
  • B. 子进程只共享代码段,数据段全部重新初始化
  • C. 父子进程完全共享内存,互相可见所有修改
  • D. 父子共享同一份物理页,任一方写入时才复制该页(写时复制 COW)

答案:D 解析:fork 只复制页表,物理页通过引用计数共享,写触发缺页异常再拷贝;这就是 fork 大内存进程仍然很快、但写入会翻倍占用的原因。详见《03-IO模型与网络编程》

85.(操作系统·中)以下哪项不是死锁的四个必要条件之一?

  • A. 时间片轮转调度
  • B. 互斥条件
  • C. 请求与保持条件
  • D. 循环等待条件

答案:A 解析:四个必要条件是互斥、请求与保持、不可剥夺、循环等待;破坏任意一个即可预防死锁,调度算法与死锁无必然关系。详见《01-多线程与并发编程》

86.(操作系统·中)用户态切换到内核态的典型时机是?

  • A. 执行任何一条算术指令
  • B. 系统调用、中断或异常
  • C. 方法调用与返回
  • D. 只有进程创建时

答案:B 解析:系统调用(read/write)、外设中断、缺页等异常会触发特权级切换并保存现场;频繁切换的开销正是 NIO/epoll、零拷贝优化的动机。详见《03-IO模型与网络编程》

87.(ZooKeeper·中)用 ZooKeeper 实现分布式锁或公平排队,最合适的节点组合是?

  • A. 持久节点 + 轮询
  • B. 只读节点 + Watch 全量列表
  • C. 临时顺序节点,各客户端监听前一个节点
  • D. 持久顺序节点 + 定时删除

答案:C 解析:临时节点随会话断开自动消失,解决持锁者宕机的死锁;顺序号保证公平,只监听前一个节点可避免羊群效应。详见《06-ZooKeeper与Nacos》

88.(ZooKeeper·中)一个 3 节点的 ZK 集群,最多能容忍几个节点宕机仍对外提供读写?

  • A. 0 个
  • B. 2 个
  • C. 3 个都可以
  • D. 1 个

答案:D 解析:过半机制要求存活节点数 > n/2:3 台需 2 台在线,故只能挂 1 台;5 台可挂 2 台。部署 4 台的容错能力和 3 台一样,还多花成本。详见《06-ZooKeeper与Nacos》

89.(ZooKeeper·中)关于 ZooKeeper 的 Watcher 机制,正确的是?

  • A. Watcher 是一次性的,事件触发后需重新注册
  • B. Watcher 触发后会持续监听该节点的每次变更
  • C. Watcher 能保证事件按写入顺序且不丢失地送达
  • D. 只有持久节点能设置 Watcher

答案:A 解析:一次性注册意味着两次变更之间的信息可能丢失,因此 ZK 不适合做可靠消息通知;Nacos 的配置推送、MQ 的削峰都用别的方案。详见《06-ZooKeeper与Nacos》

90.(框架·中)动态排序需求 ORDER BY ${orderBy} 的正确防护做法是?

  • A. 把 orderBy 用 #{} 包住即可防注入
  • B. 在 Java 层用白名单校验列名,非法值直接抛异常或回退默认排序
  • C. 依赖 MyBatis 自带的 SQL 转义函数
  • D. 只允许前端传数字,就不会有注入

答案:B 解析:ORDER BY 的列名无法用 #{} 参数化(预编译占位符只能替换值),只能字符串拼接,必须服务端白名单枚举;D 错在字段名本身也可被注入。详见《MyBatis》

91.(设计模式·中)双重检查锁单例中,实例字段必须加 volatile,原因是?

  • A. volatile 能让 new 操作变成原子操作
  • B. 不加 volatile 会导致编译错误
  • C. 保证可见性,且禁止”分配内存-初始化-赋值引用”三步重排序
  • D. 主要是为了提升加锁的性能

答案:C 解析:没有 volatile 时,其他线程可能拿到引用非 null 但尚未初始化完的半成品对象;volatile 的内存屏障禁止了这一步序颠倒。详见《07-设计模式》

92.(工具链·中)要把功能分支合并回主干且保留真实分叉历史,应选?

  • A. git rebase,历史变成一条直线
  • B. git cherry-pick
  • C. git stash
  • D. git merge,产生一个合并提交、保留分支分叉

答案:D 解析:merge 不改写历史,适合已推送的公共分支;rebase 会重写提交,对多人协作的分支执行会有风险,“已发布的历史不要 rebase”是铁律。详见《04-开发工具链-Git-Maven-Linux》

93.(工具链·中)一次 merge 已经把错误代码合进了主干并已推送,最稳妥的撤销方式是?

  • A. git revert -m 1 <合并提交>
  • B. git reset —hard HEAD~1 后强推
  • C. git commit —amend
  • D. 删掉分支重新拉

答案:A 解析:revert 生成一条反向提交、不改写历史,对协作分支安全;-m 1 表示保留主干一侧的父提交。reset+强推会让同事的本地历史错位。详见《04-开发工具链-Git-Maven-Linux》

94.(工具链·中)Maven 出现依赖版本冲突时,默认的仲裁规则是?

  • A. 版本号最大的胜出
  • B. 依赖树中路径最近的胜出,路径相同则先声明的胜出
  • C. pom 中最后一个 胜出
  • D. 由传递依赖的作者指定

答案:B 解析:先”最短路径优先”再”同深度先声明优先”;定位用 mvn dependency:tree,治理用 或 exclusions 显式固定版本。详见《04-开发工具链-Git-Maven-Linux》

95.(单元测试·中)@MockBean 与 Mockito 的 @Mock 的关键区别是?

  • A. 两者完全等价,只是包名不同
  • B. @Mock 能替换容器里的 Bean,@MockBean 不能
  • C. @MockBean 替换 Spring 容器中的 Bean 且会导致上下文缓存失效重建,@Mock 只是纯对象、需手动注入
  • D. @MockBean 只能用于 Controller 层

答案:C 解析:@MockBean 需要 SpringBootTest 上下文支持,一个类里多个 @MockBean 会显著拖慢测试;纯单元测试优先用 @Mock + @InjectMocks。详见《05-单元测试与工程质量》

96.(单元测试·中)要测试”Controller → Service → Mapper 整条链路真实装配”,最合适的注解组合是?

  • A. @WebMvcTest 单独使用
  • B. @DataJpaTest 单独使用
  • C. @ExtendWith(MockitoExtension.class)
  • D. @SpringBootTest + @AutoConfigureMockMvc

答案:D 解析:@SpringBootTest 启动完整容器做集成测试;@WebMvcTest 只加载 Web 层(Service 需 mock),@DataJpaTest 只测持久层且默认事务回滚。详见《05-单元测试与工程质量》

97.(MySQL·难)关于间隙锁(Gap Lock)与插入冲突,下列描述正确的是?

  • A. 两个事务同时对同一间隙加间隙锁不冲突,但其中一个插入该间隙会被另一个阻塞
  • B. 间隙锁之间互斥,第二个事务无法加
  • C. 间隙锁会锁住整张表
  • D. RR 与 RC 都会对范围查询加间隙锁

答案:A 解析:间隙锁只与”插入该间隙”冲突,彼此相容,所以两事务都能锁 (10,20) 区间;一旦一方插入 (10,20) 就被另一方的间隙锁挡住——并发插入死锁的常见来源。RC 下基本不加间隙锁。详见《索引-事务-锁与调优》

98.(Java基础·中)int[] a = {1, 2, 3}; int[] b = a.clone(); b[0] = 99; 之后 a[0] 的值是?

  • A. 99
  • B. 1
  • C. 编译错误
  • D. 0

答案:B 解析:数组 clone 是浅拷贝,新数组元素独立;若元素是引用类型则复制的是引用,改对象内容仍会互相影响。详见《07-设计模式》

99.(Java基础·中)关于接口中的方法,Java 8 之后说法正确的是?

  • A. 接口只能有抽象方法
  • B. default 方法必须被实现类强制覆写
  • C. 接口可以有 default 方法与 static 方法,default 方法可被实现类继承或覆写
  • D. static 方法可以被子接口继承并在实现类中直接调用

答案:C 解析:Java 8 引入 default/static 方法支持接口演进;default 可不覆写,static 属于接口本身、不被继承,只能通过接口名调用。详见《02-面向对象与核心类》

100.(Java基础·中)一个类实现两个接口,两个接口有同名同参的 default 方法,会发生什么?

  • A. 自动选择第一个接口的实现
  • B. 自动选择第二个接口的实现
  • C. 运行时抛异常
  • D. 编译错误,必须在该类中显式覆写并用 接口名.super.方法() 指定

答案:D 解析:菱形冲突编译器无法裁决,强制实现类覆写消歧,这也是 default 方法引入后的典型考点。详见《02-面向对象与核心类》

101.(Java基础·中)new BigDecimal(0.1) 与 new BigDecimal("0.1") 的区别是?

  • A. 前者以 double 的二进制近似值为准,结果不是精确的 0.1;后者按十进制字符串精确表示
  • B. 没有区别,结果相同
  • C. 前者精确、后者有误差
  • D. 前者编译报错

答案:A 解析:造 BigDecimal 必须用 String 或 valueOf,用 double 会把二进制误差一起带进来,金额计算尤忌。详见《02-面向对象与核心类》

102.(Java基础·中)自定义异常继承 RuntimeException 与继承 Exception 的关键差别是?

  • A. 前者是受检异常必须 try 或 throws
  • B. 前者无需显式 catch/throws,后者编译期强制处理
  • C. 后者是运行时异常,编译器不强制处理
  • D. 没有差别,只是命名习惯

答案:B 解析:Exception 系(非 RuntimeException)是受检异常,编译器强制处理;RuntimeException 系免检,参数校验失败常抛它。详见《04-异常-IO-反射-注解》

103.(Java基础·中)关于 try-with-resources,说法正确的是?

  • A. 任何对象都能作为资源
  • B. 必须在 finally 里手动 close,否则不会关闭
  • C. 资源必须实现 AutoCloseable,且 close 会在 try 块结束(含异常)时自动调用
  • D. 多个资源按声明顺序逆序关闭,先声明后关闭

答案:C 解析:需实现 AutoCloseable(或 Closeable),多个资源按声明逆序关闭(后声明先关);这也是它优于手写 finally 的地方。详见《04-异常-IO-反射-注解》

104.(Java基础·中)Class.forName("com.mysql.jdbc.Driver") 与 ClassLoader.loadClass(...) 的关键差别是?

  • A. 两者完全等价
  • B. forName 只能加载 JDK 自带的类
  • C. loadClass 会触发静态初始化,forName 不会
  • D. forName 默认会执行类的静态初始化块,loadClass 默认不会

答案:D 解析:forName 默认 initialize=true 会跑 static 块,这正是 JDBC 驱动注册的经典用法;loadClass 只加载不初始化。详见《02-JVM核心》

105.(Java基础·中)序列化时 serialVersionUID 不一致会导致什么?

  • A. 反序列化时抛 InvalidClassException
  • B. 序列化时抛异常
  • C. 静默忽略旧字段,正常反序列化
  • D. 编译报错

答案:A 解析:serialVersionUID 是序列化的版本契约,类结构变了而 UID 不匹配时反序列化会拒绝,显式声明可避免编译器自动生成值随环境变化。详见《04-异常-IO-反射-注解》

106.(Java基础·难)静态代码块、实例代码块、构造方法的执行顺序(首次创建对象时)是?

  • A. 构造方法 → 实例块 → 静态块
  • B. 静态块 → 实例块 → 构造方法
  • C. 实例块 → 静态块 → 构造方法
  • D. 三者顺序不确定

答案:B 解析:静态块随类初始化执行且仅一次,之后每次 new 走实例块再走构造方法,父类先于子类;面试常给继承场景问输出。详见《02-面向对象与核心类》

107.(Java基础·难)String s = "a" + "b" + "c"; 在编译期发生什么?

  • A. 创建三个临时 String 再拼接
  • B. 编译为 StringBuilder 链式调用
  • C. 编译器常量折叠,直接生成 “abc” 一个常量
  • D. 运行时用 + 逐个拼接

答案:C 解析:字面量相加编译器直接折叠为一个常量;只有含变量时才会编译为 StringBuilder,这是「字符串拼接性能」题的关键前提。详见《02-面向对象与核心类》

108.(Java基础·难)方法重载的解析(选择哪个重载版本)发生在什么阶段?

  • A. 运行时,根据实际对象类型动态选择
  • B. 编译期和运行时各选一次
  • C. 运行时,根据参数个数动态选择
  • D. 编译期,根据引用变量的静态类型选择

答案:D 解析:重载是编译期静态分派(看静态类型),重写是运行期动态分派(看实际类型);重载版本的参数若是父类与子类,选的是编译期那个。详见《02-面向对象与核心类》

109.(Java基础·难)以下哪种情况不会触发类的初始化?

  • A. 通过数组定义引用类,如 MyClass[] arr = new MyClass[5]
  • B. 读取一个类的静态字段(非常量)
  • C. new 一个该类的实例
  • D. 通过反射调用该类的静态方法

答案:A 解析:数组定义只创建数组对象,不初始化元素类;B/C/D 都会触发初始化。另有两条边界:static final 基本类型常量在编译期内联、同样不触发;通过子类访问父类静态字段,初始化的是父类。详见《02-JVM核心》

110.(集合·中)ArrayList 与 LinkedList 做头部插入 10000 次,性能差异的根本原因是?

  • A. LinkedList 线程安全所以更慢
  • B. ArrayList 头插需要整体移动元素 O(n),LinkedList 只需改指针 O(1)
  • C. LinkedList 更省内存
  • D. ArrayList 不支持头部插入

答案:B 解析:ArrayList 底层数组,头插要 System.arraycopy 搬移;LinkedList 双向链表改前后指针即可,但按索引访问却是 O(n)。详见《03-集合与泛型》

111.(集合·中)HashMap 扩容时(JDK 1.8)对旧桶链表的处理方式是?

  • A. 重新计算每个 key 的 hash 再分配
  • B. 全部重新插入到新表头
  • C. 利用 hash & oldCap 判断高位,把链表拆成「原位置」与「原位置+oldCap」两条
  • D. 保持链表不动,只扩大数组

答案:C 解析:容量为 2 的幂时,元素新位置只可能是原位或原位+oldCap,1.8 借此省去重算 hash 并避免 1.7 头插成环。详见《03-集合与泛型》

112.(集合·中)遍历 HashMap 时想按插入顺序输出,应该用?

  • A. HashMap
  • B. Hashtable
  • C. TreeMap
  • D. LinkedHashMap

答案:D 解析:LinkedHashMap 在哈希结构上加了双向链表维护插入顺序(accessOrder=true 时变访问顺序,天然可做 LRU);TreeMap 按 key 排序。详见《03-集合与泛型》

113.(集合·中)TreeMap 的 key 必须满足什么条件?

  • A. 必须实现 Comparable 或构造时传入 Comparator
  • B. 必须实现 hashCode
  • C. 必须是 String
  • D. 必须重写 equals 即可

答案:A 解析:TreeMap 靠比较排序,key 需可比较,否则 put 时抛 ClassCastException;它用比较结果代替 equals 判等。详见《03-集合与泛型》

114.(集合·中)关于 Iterator.remove() 与 Collection.remove(),说法正确的是?

  • A. 两者在遍历中等价
  • B. for-each 遍历时调用集合的 remove 会抛 ConcurrentModificationException(某些集合行为未定义),Iterator.remove 才是安全做法
  • C. Iterator.remove 会抛异常
  • D. 只有 Map 支持 Iterator.remove

答案:B 解析:for-each 底层是迭代器,直接改集合会破坏 modCount 校验;用迭代器自己的 remove,或 Java 8 的 removeIf。详见《03-集合与泛型》

115.(集合·难)HashMap 中两个不同对象 hash 值相同但 equals 为 false,会怎样?

  • A. 后者覆盖前者
  • B. 抛异常
  • C. 两者共存于同一桶,通过链表/红黑树区分,查找时先比 hash 再比 equals
  • D. 只保留先插入的

答案:C 解析:哈希冲突是常态,定位桶后再用 equals 精确区分;这就是「重写 equals 必须重写 hashCode」的另一面——hash 相同不代表相等。详见《03-集合与泛型》

116.(集合·难)ConcurrentHashMap(JDK 1.8)保证线程安全的核心手段是?

  • A. 全表加一把 synchronized 锁
  • B. 用 Segment 分段锁
  • C. 读操作也全程加锁
  • D. CAS + synchronized 锁单个桶头节点,配合 volatile 保证可见性

答案:D 解析:1.8 抛弃 Segment,锁粒度细到桶(首节点),空桶用 CAS 插入,扩容支持多线程协助迁移;1.7 才是分段锁。详见《03-集合与泛型》

117.(集合·难)以下代码输出的顺序是?Set<String> s = new LinkedHashSet<>(); s.add("b"); s.add("a"); s.add("c"); s.add("a");

  • A. b, a, c
  • B. a, b, c
  • C. c, a, b
  • D. b, c, a

答案:A 解析:LinkedHashSet 保持插入顺序且去重,重复的 “a” 不改变原位置也不追加,输出按首次插入序。详见《03-集合与泛型》

118.(集合·难)ArrayList 的 subList(1, 3) 返回的视图,对它的修改会怎样?

  • A. 抛 UnsupportedOperationException
  • B. 直接反映到原列表(它是视图而非副本),原列表结构变化后视图操作可能抛 ConcurrentModificationException
  • C. 生成独立副本,互不影响
  • D. 只能读不能写

答案:B 解析:subList 是原列表的视图,改视图即改原表;这是踩坑高发区,需要副本时必须 new ArrayList<>(list.subList(…))。详见《03-集合与泛型》

119.(Java8·中)Stream 中 map 与 flatMap 的区别是?

  • A. map 可以改变流的大小,flatMap 不能
  • B. 两者完全等价
  • C. map 是一对一转换,flatMap 把每个元素展开成流再合并(一对多/扁平化)
  • D. flatMap 只能处理字符串

答案:C 解析:map 元素数不变(1→1),flatMap 先 map 再 flat(1→N),典型场景是 List<List> 打平。详见《05-Lambda-Stream与Java8新特性》

120.(Java8·中)Stream 的 peek 方法在什么情况下不会执行?

  • A. 永远会执行
  • B. 并行流中永远不执行
  • C. 只处理基本类型时
  • D. 后面没有终端操作时(惰性求值,中间操作不触发)

答案:D 解析:中间操作惰性,只有终端操作才会拉动整条流水线;peek 常用于调试,别拿它做副作用业务。详见《05-Lambda-Stream与Java8新特性》

121.(Java8·中)关于并行流 parallelStream() 的默认线程池,说法正确的是?

  • A. 使用公共 ForkJoinPool.commonPool(),可用 -Djava.util.concurrent.ForkJoinPool.common.parallelism 调大小
  • B. 每次调用新建一个线程池
  • C. 使用 Tomcat 线程池
  • D. 使用 newCachedThreadPool

答案:A 解析:并行流共享 commonPool,默认并行度是 CPU 核数-1;阻塞型任务别用并行流,会拖垮整个公共池。详见《05-Lambda-Stream与Java8新特性》

122.(Java8·难)Collectors.groupingBy 默认返回的 Map 是什么类型?

  • A. TreeMap
  • B. HashMap,且默认值类型是 List
  • C. LinkedHashMap
  • D. ConcurrentHashMap

答案:B 解析:默认 HashMap+List;要保序用三参重载传 LinkedHashMap::new,要下游聚合传 counting()/mapping() 等。详见《05-Lambda-Stream与Java8新特性》

123.(Java8·难)以下代码输出的结果是?Optional<String> o = Optional.of(null);

  • A. 创建成功,o.isEmpty() 为 true
  • B. 编译错误
  • C. 立即抛出 NullPointerException
  • D. 创建成功但 get() 返回 null

答案:C 解析:of 不接受 null(空值用 ofNullable),这是刻意设计——让空指针尽早暴露而非藏进 Optional;get() 前应先 isPresent 或用 orElse。详见《05-Lambda-Stream与Java8新特性》

124.(Java基础·易)Java 中 switch 的 case 标签可以使用的类型不包括?

  • A. byte/short/char/int
  • B. 枚举
  • C. String(Java 7+)
  • D. double

答案:D 解析:switch 支持整型(含包装类)、枚举、String 与 Java 17 的模式匹配,浮点与 long 不行(long 自 Java 14 起在部分场景可用,但 double 始终不支持语法语义是明确考点)。详见《01-环境搭建与基础语法》

125.(Java基础·中)String s = "hello"; s.concat(" world"); System.out.println(s); 输出?

  • A. hello
  • B. hello world
  • C. 编译错误
  • D. hello world 但 s 已变

答案:A 解析:String 不可变,concat 返回新串,返回值未接收则原引用不变;这类「忘了接返回值」是常见低级 bug。详见《02-面向对象与核心类》

126.(集合·中)HashMap 的初始容量 16 与负载因子 0.75 意味着什么?

  • A. 存满 16 个元素才扩容
  • B. 元素数超过 16×0.75=12 时扩容为两倍
  • C. 永远不扩容
  • D. 初始容量是 12

答案:B 解析:阈值 = 容量×负载因子,达到阈值触发 resize,容量翻倍;负载因子权衡空间与冲突,0.75 是官方折中值。详见《03-集合与泛型》

127.(集合·中)Collections.unmodifiableList(list) 与 List.of(...) 的区别是?

  • A. 两者都是不可变且都不允许 null
  • B. List.of 可以继续 add
  • C. unmodifiableList 是原列表的只读视图(原列表变了它也跟着变),List.of 是真正不可变且不允许 null
  • D. unmodifiableList 会复制一份数据

答案:C 解析:只读视图仍被原表牵动,防御性场景要真不可变;List.of 拒绝 null 且结构绝对不变。详见《03-集合与泛型》

128.(Java基础·难)泛型擦除后,List<String> 与 List<Integer> 在运行时的 getClass() 结果是?

  • A. 分别是 Class<List> 与 Class<List>
  • B. 一个是 List 一个是 Collection
  • C. 编译错误
  • D. 都是同一个 Class 对象(如 java.util.ArrayList),因为泛型在运行时被擦除

答案:D 解析:泛型仅存在于编译期(类型检查与自动转换),运行时都是裸类型,这也是不能 new T[]、不能对泛型做 instanceof 的原因。详见《03-集合与泛型》

129.(Java8·中)Predicate<T> 的 and/or/negate 组合,p.negate().and(p) 对任意输入的结果是?

  • A. 恒为 false
  • B. 恒为 true
  • C. 等于 p 本身
  • D. 编译错误

答案:A 解析:negate 取反后与原判定做逻辑与,两个条件互斥,任何输入都不可能同时成立。详见《05-Lambda-Stream与Java8新特性》

130.(并发·易)线程 t 的任务体是 Thread.sleep(5000),主线程在 t 启动 300ms 后打印 t.getState(),结果是?

  • A. BLOCKED
  • B. WAITING
  • C. TIMED_WAITING
  • D. RUNNABLE

答案:C 解析:带超时的 sleep 让线程进入 TIMED_WAITING;BLOCKED 专指等待 synchronized 锁,WAITING 是无超时的 wait/park。详见《01-多线程与并发编程》

131.(并发·易)两个线程分别用 new A() 和 new A() 调用同一个 static synchronized 方法,结果是?

  • A. 可以并行执行,因为实例对象不同
  • B. 只有同一个实例的调用才互斥
  • C. 抛 IllegalMonitorStateException
  • D. 互斥执行,因为锁的是同一个 Class 对象

答案:D 解析:静态方法没有 this,同步锁是类对应的 Class 对象,与实例无关;只有实例方法才锁 this。详见《01-多线程与并发编程》

132.(并发·易)关于守护线程,下列说法正确的是?

  • A. 所有非守护线程结束后 JVM 就会退出,即使还有守护线程在运行
  • B. setDaemon(true) 可以在 start() 之后调用
  • C. 守护线程的 finally 块一定会被执行
  • D. main 线程默认是守护线程

答案:A 解析:守护线程不阻止 JVM 退出,被放弃时 finally 不保证执行;setDaemon 必须在 start 前调用,main 是非守护线程。详见《01-多线程与并发编程》

133.(并发·易)10 个线程各对同一变量自增 1000 次:用 int count 与用 AtomicInteger,最终结果分别是?

  • A. 两者都一定是 10000
  • B. int 一定是 10000,AtomicInteger 可能小于 10000
  • C. int 通常小于 10000 且每次运行可能不同,AtomicInteger 一定是 10000
  • D. 两者都可能小于 10000

答案:C 解析:count++ 是读-改-写三步,多线程下会丢更新;AtomicInteger 靠 CAS 保证单次自增原子,计数精确。详见《01-多线程与并发编程》

134.(并发·易)以下哪种线程池最容易因任务无界堆积而 OOM?

  • A. new ThreadPoolExecutor(4, 8, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100))
  • B. new ThreadPoolExecutor(2, 4, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10))
  • C. new ThreadPoolExecutor(4, 8, 60, TimeUnit.SECONDS, new SynchronousQueue<>())
  • D. Executors.newFixedThreadPool(4)

答案:D 解析:newFixedThreadPool 用无界 LinkedBlockingQueue,队列永不溢出、任务持续堆积直到 OOM,所以规约要求手动 new ThreadPoolExecutor。详见《01-多线程与并发编程》

135.(并发·中)线程池 corePoolSize=2、maximumPoolSize=4、workQueue 为 ArrayBlockingQueue(2)、默认拒绝策略,连续提交 8 个一直阻塞不结束的任务,结果是?

  • A. 8 个任务全部进队列排队
  • B. 4 个在执行、2 个在队列、2 个被拒绝抛 RejectedExecutionException
  • C. 2 个在执行、6 个在队列
  • D. 2 个在执行、4 个被拒绝、2 个在队列

答案:B 解析:先占满 2 个核心线程,再塞满容量 2 的队列,然后扩到 maximum 共 4 个线程,剩余 2 个走 AbortPolicy 抛异常。详见《01-多线程与并发编程》

136.(并发·中)关于 ReentrantLock,下列说法错误的是?

  • A. 一个 ReentrantLock 实例只能创建一个 Condition 对象
  • B. 必须手动调用 unlock,通常写在 finally 中
  • C. 构造时传入 true 可以得到公平锁
  • D. lockInterruptibly() 支持在等待锁时响应中断

答案:A 解析:一个 ReentrantLock 可以 newCondition() 出多个 Condition 做精准唤醒,这正是它相较 wait/notify 的优势。详见《01-多线程与并发编程》

137.(并发·中)线程 A 先执行 x = 1;(x 为普通 int),紧接着执行 flag = true;(flag 为 volatile boolean);线程 B 在 while (!flag) {} 之后打印 x,输出是?

  • A. 一定是 0
  • B. 可能是 0 也可能是 1,取决于 CPU 核数
  • C. 编译不通过,volatile 不能修饰 boolean
  • D. 一定是 1

答案:D 解析:volatile 写 happens-before 读到该值的读,而 A 中 x=1 又先于 volatile 写,经传递性 x 的修改对 B 必然可见。详见《01-多线程与并发编程》

138.(并发·中)CompletableFuture.supplyAsync(() -> { sb.append("1"); return "x"; }).thenApply(s -> { sb.append("2"); return s; }).thenAccept(s -> sb.append("3")).join(); 返回后主线程再执行 sb.append("4"),最终 sb 的内容是?

  • A. 1234
  • B. 4123
  • C. 2134
  • D. 无法确定,thenApply 可能与 supplyAsync 并行执行

答案:A 解析:join 会阻塞到整条链执行完毕,1→2→3 按阶段先后串行完成,主线程随后才追加 4;链式阶段的依赖关系保证不了乱序。详见《01-多线程与并发编程》

139.(并发·中)Semaphore s = new Semaphore(2); 两个线程已各 acquire 一次且都没 release,此时第三个线程调用 s.acquire() 会?

  • A. 立即返回,许可数变为 -1
  • B. 阻塞,直到有线程 release 归还许可
  • C. 抛 IllegalStateException
  • D. 抢占其他线程已持有的许可

答案:B 解析:许可耗尽后 acquire 会进入 AQS 队列挂起,release 时被唤醒;不想无限等待可改用带超时的 tryAcquire。详见《01-多线程与并发编程》

140.(并发·中)CAS 的 ABA 问题指的是什么?常用什么方案解决?

  • A. 两个线程同时 CAS 导致都失败,需要用自旋重试解决
  • B. 值从 A 变成 B 又变回 A,CAS 误以为从未变过;用 AtomicStampedReference 加版本号解决
  • C. CAS 只能操作 int 类型,需要用 AtomicReference 解决
  • D. CAS 失败自旋消耗 CPU,需要改用 synchronized 解决

答案:B 解析:ABA 的坑在于中间状态被抹平;AtomicStampedReference 用版本戳识别「变过又变回来」。自旋开销是 CAS 的另一个独立问题。详见《01-多线程与并发编程》

141.(并发·中)JDK 1.8 的 ConcurrentHashMap 执行 put 时,对空桶与非空桶分别采用?

  • A. 都用 synchronized 锁住对应的 Segment 段
  • B. 空桶用 CAS 插入,非空桶用 synchronized 锁住桶的头节点
  • C. 空桶用 synchronized,非空桶用 CAS
  • D. 全程用 ReentrantLock 锁住整个 table

答案:B 解析:1.8 抛弃 Segment 分段锁,桶为空时 CAS 无锁写入,发生冲突才 synchronized 锁单个桶头,锁粒度比 1.7 更细。详见《01-多线程与并发编程》

142.(并发·中)关于 CopyOnWriteArrayList,下列说法错误的是?

  • A. 每次写操作都复制一份新数组,写开销较大
  • B. 迭代器基于快照,遍历时不会抛 ConcurrentModificationException
  • C. 适合读多写少的场景,如监听器列表
  • D. 迭代过程中能立刻读到其他线程刚写入的新元素

答案:D 解析:读写分离带来弱一致性,迭代器拿到的是创建时的旧数组快照,看不到之后的写入,这正是它写少读多才划算的原因。详见《01-多线程与并发编程》

143.(并发·中)线程池中某任务往 ThreadLocal 里 set 了值却没有 remove,同一线程执行的下一个任务调用 get() 会得到?

  • A. 上一个任务残留的值
  • B. null,因为线程复用时 JVM 会重置 ThreadLocalMap
  • C. 抛 IllegalStateException
  • D. 取决于任务是用 submit 还是 execute 提交

答案:A 解析:ThreadLocalMap 挂在线程对象上并随线程复用,不会自动清空,这是线程池场景必须在 finally 中 remove 的原因。详见《01-多线程与并发编程》

144.(并发·中)预防死锁最常用的手段是?

  • A. 加大线程池的线程数
  • B. 用 volatile 修饰锁对象
  • C. 把多把锁合并成一把大锁并延长持锁时间
  • D. 让所有线程按相同的全局顺序获取锁

答案:D 解析:死锁四条件中最容易破坏的是「循环等待」,统一加锁顺序即可;tryLock 带超时也是常用兜底。加大线程数只会提高发生概率。详见《01-多线程与并发编程》

145.(并发·中)线程池 shutdownNow() 的返回值是?

  • A. 队列中尚未开始执行的任务列表
  • B. void,没有返回值
  • C. 已经执行完成的任务数
  • D. 表示线程池是否成功终止的布尔值

答案:A 解析:shutdownNow 会尝试中断执行中的任务并返回未执行的任务列表以便补偿处理;温和关闭等任务跑完的是 shutdown。详见《01-多线程与并发编程》

146.(并发·难)AQS 共享模式中,子类实现的 tryAcquireShared 返回值语义是?

  • A. 负值表示获取成功,0 表示失败,正值表示重入次数
  • B. 负值表示获取失败需要排队,0 表示获取成功但后继线程无法再获取,正值表示获取成功且后继也可能成功
  • C. 只需区分是否为 0,其余取值没有意义
  • D. 返回值就是当前 state 的值,仅供调试打印

答案:B 解析:共享模式下返回值同时承担「告知后继节点能否继续获取」的职责,CountDownLatch 计数归零时返回正数唤醒整条队列。详见《01-多线程与并发编程》

147.(并发·难)关于 synchronized 的偏向锁撤销,下列说法正确的是?

  • A. 只要出现第二个线程竞争,就会立即膨胀为重量级锁
  • B. 偏向锁一旦被撤销,该对象就永远无法再被偏向
  • C. 撤销偏向需要在全局安全点进行,批量重偏向与批量撤销正是为降低这笔开销引入的优化
  • D. 偏向锁无法通过 JVM 参数关闭

答案:C 解析:撤销要先等 STW 安全点,代价高,于是有了批量重偏向/撤销;竞争时先升级为轻量级锁而非直接重量级。详见《01-多线程与并发编程》

148.(并发·难)CMS 并发标记阶段使用的增量更新(incremental update)写屏障,记录的是?

  • A. 被删除的旧引用,即所谓原始快照
  • B. 所有新创建出来的对象,防止它们被漏标
  • C. 新插入的引用,并把新增该引用的黑色对象重新变灰,避免存活对象被漏标
  • D. 跨代引用的卡表条目

答案:C 解析:增量更新破坏「黑色对象新增指向白色对象的引用」这一漏标条件,屏障记录新引用、把该黑对象重新变灰待扫;记录被删除的旧引用则是 G1 的 SATB 思路。详见《02-JVM核心》

149.(并发·难)ThreadLocalMap 的 key 已经是弱引用,为什么仍会内存泄漏、还必须手动 remove()?

  • A. 弱引用在 Java 中对 GC 没有实际作用,key 永远不会被回收
  • B. ThreadLocalMap 是定长数组,槽位迟早会被写满
  • C. key 是弱引用但 value 是强引用,key 被回收后 Entry 变成 key=null 而 value 仍被线程强引用,且 get/set 只顺带清理命中的少数槽位
  • D. 弱引用只在 Full GC 时才会被回收,平时等价于强引用

答案:C 解析:弱引用只解决了 key 的回收,value 仍被长期存活的线程池线程强引用;探测式清理只覆盖命中的槽位,所以必须 remove。详见《01-多线程与并发编程》

150.(并发·难)ConcurrentHashMap 用 baseCount + CounterCell[] 而不是单个 AtomicLong 来统计 size,主要目的是?

  • A. 让 size() 的返回值具有强一致性
  • B. 把不同线程的计数分散到不同槽位,减少单点 CAS 自旋与缓存行伪共享带来的冲突
  • C. 支持扩容时的多线程协助迁移
  • D. 让 size() 的时间复杂度降为 O(1)

答案:B 解析:这与 LongAdder 的分段累加同源,热点计数被打散到多个 Cell 上;size() 仍是遍历求和,只保证弱一致。详见《01-多线程与并发编程》

151.(JVM·易)对象头中的 Mark Word 不包含以下哪项信息?

  • A. 对象的实例字段值
  • B. 对象的哈希码
  • C. GC 分代年龄
  • D. 锁状态标志位

答案:A 解析:Mark Word 存放哈希码、分代年龄、锁标志等运行时数据,实例字段值属于实例数据区。详见《02-JVM核心》

152.(网络·易)下列场景中最适合使用 UDP 的是?

  • A. 下载一份必须完整无损的安装包
  • B. 实时视频会议的音视频流传输
  • C. 数据库客户端与数据库服务器之间的通信
  • D. 通过 HTTP 浏览网页

答案:B 解析:UDP 无连接、开销小、可容忍少量丢包,适合实时音视频;其余场景都要求可靠有序,应当用 TCP。详见《03-IO模型与网络编程》

153.(操作系统·易)下列进程间通信方式中,数据无需在用户态与内核态之间反复拷贝、速度最快的是?

  • A. 匿名管道
  • B. 消息队列
  • C. 共享内存
  • D. 信号(signal)

答案:C 解析:共享内存把同一块物理页映射进两个进程的地址空间,读写不再经过内核拷贝,代价是要自行用信号量同步。详见《03-IO模型与网络编程》

154.(JVM·难)同一个 .class 文件被两个不同的类加载器分别加载,下列说法正确的是?

  • A. 得到同一个 Class 对象,两个加载器共享该类的静态变量
  • B. 得到两个 Class 对象,但两者之间可以正常互相赋值
  • C. 得到两个 Class 对象,但 instanceof 判断依然为 true
  • D. 得到两个互不兼容的 Class 对象,互相赋值会抛 ClassCastException

答案:D 解析:类加载器构成命名空间,同一个 class 被不同加载器加载会得到两个独立 Class,instanceof 为 false,转型直接抛异常。详见《02-JVM核心》

155.(网络·中)TCP 主动关闭方在发完最后一个 ACK 后进入 TIME_WAIT 并等待 2MSL,主要目的是?

  • A. 确保这个 ACK 丢失时对端重传的 FIN 能被响应,并让本次连接的旧报文在网络中消亡
  • B. 等待对端把尚未发完的剩余数据发送完毕
  • C. 等待操作系统回收本地端口号资源
  • D. 防止对端重发 SYN 重新建立连接

答案:A 解析:2MSL 保证最后 ACK 丢失时可重发,同时让迟到报文过期,避免串到之后复用相同四元组的新连接上。详见《03-IO模型与网络编程》

156.(操作系统·易)关于僵尸进程,下列说法正确的是?

  • A. 父进程先于子进程退出,子进程被 init 收养,因此称为僵尸进程
  • B. 子进程已退出但父进程尚未调用 wait/waitpid 回收其退出状态,进程表项仍被占用
  • C. 僵尸进程会持续占用 CPU 空转,只能靠 kill -9 清除
  • D. 僵尸进程占用大量物理内存,是内存泄漏的主要来源

答案:B 解析:退出状态未被父进程回收,进程表项残留;它不占 CPU 与内存,kill -9 也无效,需父进程 wait。A 描述的是孤儿进程。详见《03-IO模型与网络编程》

157.(JVM·中)类加载的准备阶段,对 static int a = 1; static final int B = 2; 这两个字段的处理是?

  • A. a 与 B 都只赋零值,到初始化阶段才分别变成 1 和 2
  • B. a 直接赋为 1,B 先赋零值,到初始化阶段再赋为 2
  • C. a 赋零值(初始化阶段才变成 1),B 在准备阶段即被赋为 2
  • D. a 与 B 都在准备阶段直接赋为最终值

答案:C 解析:普通静态变量在准备阶段只赋零值;被 static final 修饰的编译期常量带 ConstantValue 属性,准备阶段就完成赋值。详见《02-JVM核心》

158.(网络·中)关于 epoll 的水平触发(LT)与边缘触发(ET),下列说法正确的是?

  • A. LT 只在状态变化时通知一次,ET 则只要还有数据就反复通知
  • B. 两者行为完全一致,只是 ET 的性能更好
  • C. ET 模式只能用于监听套接字,不能用于已建立连接的套接字
  • D. LT 只要缓冲区还有未读完的数据就持续通知,ET 仅在状态变化时通知一次,因此 ET 必须一次读完

答案:D 解析:LT 反复提醒直到处理干净、编程简单;ET 只报一次状态变化,必须循环读到 EAGAIN,否则剩余数据不再通知。详见《03-IO模型与网络编程》

159.(操作系统·中)进程切换的开销明显大于线程切换,主要原因是?

  • A. 进程切换要更换页表、切换虚拟地址空间,导致 TLB 与缓存大面积失效
  • B. 进程切换必须销毁并重建内核栈
  • C. 进程切换会触发一次垃圾回收
  • D. 进程切换一定伴随磁盘 I/O

答案:A 解析:同进程内的线程共享地址空间,切换只需换栈与寄存器;进程切换还要换页表,TLB 失效后访存代价显著上升。详见《03-IO模型与网络编程》

160.(JVM·中)关于 TLAB(Thread Local Allocation Buffer),下列说法正确的是?

  • A. 它位于老年代,专门用于分配大对象
  • B. 它是每个线程在 Eden 中预先划出的一小块私有区域,避免多线程分配对象时争抢同一个指针
  • C. 它位于元空间,用于缓存类元数据
  • D. 它的作用是缓存字符串常量,减少重复创建

答案:B 解析:TLAB 是 Eden 内线程私有的分配缓冲,分配在本地指针上完成,用完再申请新的,避免指针碰撞的并发竞争。详见《02-JVM核心》

161.(网络·易)HTTP 响应的 502 与 504 状态码分别表示?

  • A. 502 请求参数错误,504 未认证
  • B. 502 服务器内部错误,504 资源不存在
  • C. 502 网关收到了上游服务器的无效响应,504 网关等待上游响应超时
  • D. 502 服务不可用,504 重定向次数过多

答案:C 解析:5xx 属服务端错误:502 Bad Gateway 是上游返回了无效响应,504 是网关等上游超时,两者常见于 Nginx 反代场景。详见《03-IO模型与网络编程》

162.(操作系统·中)关于虚拟内存与缺页中断,下列说法正确的是?

  • A. 缺页中断说明程序有 bug,正常程序运行中不应出现
  • B. 虚拟内存让所有进程共享同一份地址空间,便于互相访问数据
  • C. 虚拟内存的总大小不能超过物理内存大小
  • D. 访问的页不在物理内存时触发缺页中断,由内核调入该页后重新执行刚才那条指令

答案:D 解析:缺页是按需调页的正常机制而非错误,它支撑了内存超售与进程隔离,代价是一次访问可能多花上百微秒。详见《03-IO模型与网络编程》

163.(JVM·中)关于类初始化的触发时机,下列说法正确的是?

  • A. 访问被 static final 修饰且编译期可确定值的常量,不会触发该类的初始化
  • B. 通过子类引用父类的静态字段时,子类也会被初始化
  • C. 执行 new Foo[10] 定义数组会触发 Foo 类的初始化
  • D. 只要调用了 ClassLoader.loadClass(),类就完成了初始化

答案:A 解析:编译期常量在准备阶段已进常量池,读取不触发初始化;数组定义、父类静态字段、loadClass 都只到加载与链接阶段。详见《02-JVM核心》

164.(网络·难)线上服务执行 ss -ant 后显示大量连接停留在 CLOSE_WAIT,下列判断最准确的是?

  • A. 说明本机是主动关闭方,应开启 tcp_tw_reuse 缓解
  • B. 对端已发来 FIN 且本端内核已确认,但应用程序迟迟没调用 close(),通常是连接或流未关闭的代码缺陷
  • C. 说明本机端口即将耗尽,应调大本地端口范围
  • D. 这是网络拥塞造成的,应调大拥塞窗口

答案:B 解析:CLOSE_WAIT 是”收到对方 FIN、本端尚未 close”的等待态,堆积即代码没关连接;主动关闭方的残留态是 TIME_WAIT。详见《03-IO模型与网络编程》

165.(操作系统·中)要求所有线程都按资源编号从小到大的顺序申请锁,这一做法破坏了死锁四个必要条件中的?

  • A. 互斥条件
  • B. 请求与保持条件
  • C. 循环等待条件
  • D. 不可剥夺条件

答案:C 解析:全局统一的加锁顺序使环路无法形成,破坏的是循环等待;一次性申请全部资源破坏的才是请求与保持。详见《03-IO模型与网络编程》

166.(JVM·难)G1 的 SATB(原始快照)与 CMS 的增量更新在并发标记中的区别是?

  • A. SATB 记录新建立的引用,增量更新记录被删除的引用
  • B. 两者都只记录新建立的引用,区别仅在于记录所用的数据结构不同
  • C. SATB 用于标记新生代,增量更新用于标记老年代
  • D. SATB 在引用被删除前记录旧引用,增量更新在引用被赋值后记录新引用

答案:D 解析:SATB 靠写前屏障保留删除前的旧值,维持标记开始时的对象图快照;增量更新靠写后屏障记录新引用,重新标记时再扫描。详见《02-JVM核心》

167.(网络·难)Charles、Fiddler 这类抓包工具能解密 HTTPS 流量,其前提是?

  • A. 客户端信任了工具自签发的根证书,工具分别与客户端、服务端建立两条独立的 TLS 连接
  • B. 工具拿到了服务端证书对应的私钥
  • C. 工具与客户端恰好使用了相同的会话密钥
  • D. 只要与客户端处于同一局域网内,即可自动解密

答案:A 解析:中间人分别与客户端和服务端各建一条 TLS 连接,用自签根证书骗客户端信任;证书校验一旦被绕过,流量即为明文。详见《03-IO模型与网络编程》

168.(操作系统·难)某单线程计算任务在单核上耗时 100 秒;改成 4 个无共享数据的线程在 4 核上运行后实测 28 秒(理论 25 秒),多出来的时间最主要来自?

  • A. 线程创建与销毁的系统调用开销
  • B. 线程调度与上下文切换、多核间缓存一致性(缓存行失效、伪共享)带来的额外开销
  • C. 磁盘 I/O 等待
  • D. JVM 的类加载过程

答案:B 解析:创建销毁是一次性成本,主要损耗在切换与跨核缓存同步;这也解释了多线程加速比为何总达不到线性。详见《03-IO模型与网络编程》

169.(JVM·难)关于 Minor GC 前的空间分配担保,下列说法正确的是?

  • A. 只要老年代剩余空间小于新生代对象总大小,就一定先触发一次 Full GC
  • B. 分配担保指把新创建的对象直接分配到老年代
  • C. JDK 6 Update 24 之后,只要老年代最大可用连续空间大于新生代对象总大小或历次晋升平均大小,就允许先尝试 Minor GC,否则才 Full GC
  • D. 分配担保只在 Serial 收集器上生效,G1 已取消该机制

答案:C 解析:担保是 Minor GC 前的安全检查,防的是晋升对象无处安放;HandlePromotionFailure 参数在该版本之后已不再生效。详见《02-JVM核心》

170.(网络·中)压测客户端使用短连接,观测到 TIME_WAIT 数量暴涨并触发本地端口告警,最直接有效的组合措施是?

  • A. 调大 net.ipv4.tcp_max_syn_backlog
  • B. 调大 net.ipv4.tcp_fin_timeout,延长等待时间
  • C. 关闭 net.ipv4.tcp_syncookies
  • D. 开启 net.ipv4.tcp_tw_reuse,并改用连接池复用长连接

答案:D 解析:TIME_WAIT 落在主动关闭方,短连接使它不断堆积;长连接从源头减少连接数,tcp_tw_reuse 让出站连接可复用 TIME_WAIT 端口。详见《03-IO模型与网络编程》

171.(操作系统·难)top 中某个 Java 进程的 CPU 占用显示为 380%,最合理的解释是?

  • A. top 以单核为 100% 累加,380% 表示该进程的多个线程同时在约 4 个核上运行
  • B. 数据显示异常,单进程不可能超过 100%
  • C. 该进程陷入了内核态忙等待
  • D. 该进程正在进行频繁的 Full GC

答案:A 解析:Linux 的进程 CPU 占用是多核累加值,超过 100% 属正常;要进一步定位可 top -Hp 看线程级占用再对照 jstack。详见《02-JVM核心》

172.(JVM·难)把 G1 的 -XX:MaxGCPauseMillis 从 200 调到 50 后,停顿确实变短,但吞吐量明显下降,最合理的解释是?

  • A. 停顿目标会让 G1 在压力下改用 Serial 收集器兜底
  • B. 停顿目标越小,单次可回收的 Region 越少,GC 次数变多,GC 总开销上升
  • C. 调小该参数会禁用并发标记线程
  • D. 调小该参数会强制每次 Young GC 都回收老年代

答案:B 解析:G1 依据停顿目标挑选回收的 Region 数量,目标越激进每次回收越少、频率越高;停顿与吞吐本就需要权衡。详见《02-JVM核心》

173.(网络·中)TCP 慢启动阶段,拥塞窗口 cwnd 的变化方式是?

  • A. 每个 RTT 增加 1 个报文段,直到发生超时
  • B. 由接收方通告的接收窗口 rwnd 决定,与网络状况无关
  • C. 每个 RTT 翻倍(指数增长),达到慢启动阈值 ssthresh 后转为拥塞避免的线性增长
  • D. 始终保持初始值不变,直到发生丢包才调整

答案:C 解析:慢启动从 1 个 MSS 起指数扩张,到 ssthresh 转线性;丢包后将 ssthresh 减半,再进入快速重传与快速恢复。详见《03-IO模型与网络编程》

174.(JVM·中)jstat -gcutil <pid> 1000 显示老年代使用率长期高于 98%、Full GC 次数不断上涨,但每次回收后使用率几乎不下降。最可能的原因是?

  • A. 新生代太小,对象过早晋升到老年代
  • B. SurvivorRatio 设置不合理,Survivor 区过小
  • C. 元空间不足导致频繁触发 Full GC
  • D. 存在内存泄漏,大量对象仍被引用而无法回收

答案:D 解析:回收后占用不降说明对象依然可达,是典型泄漏特征;应导出堆快照用 MAT 找 Retained Size 最大的对象链。详见《02-JVM核心》

175.(JVM·中)用 -Xmx512m -XX:MaxMetaspaceSize=16m 启动的程序,循环用 CGLIB 动态生成类并把 Class 对象存入静态集合,最可能先抛出的是?

  • A. java.lang.OutOfMemoryError: Metaspace
  • B. java.lang.OutOfMemoryError: Java heap space
  • C. java.lang.StackOverflowError
  • D. java.lang.OutOfMemoryError: Direct buffer memory

答案:A 解析:类元数据存放在元空间,静态集合又阻止了 Class 卸载,16m 的元空间上限最先被击穿,报错信息即为 Metaspace。详见《02-JVM核心》

176.(MySQL·易)InnoDB 的 B+ 树把完整行数据只放在叶子节点,这样做最直接的好处是?

  • A. 单个数据页能容纳更多键值,树更矮,定位一行所需的磁盘 IO 更少
  • B. 索引不再需要维护键值的排序
  • C. 范围查询不必再走叶子链表,在非叶子层就能扫完整个区间
  • D. 二级索引因此不必再存储主键值

答案:A 解析:非叶子只存键与页指针,一页可放上千项,三层即可覆盖千万级数据;叶子间的双向链表才是范围查询快的另一原因。详见《索引-事务-锁与调优》

177.(MySQL·易)建表时,金额、成绩这类要求精确的小数字段应优先选用哪种类型?

  • A. FLOAT
  • B. DOUBLE
  • C. DECIMAL
  • D. VARCHAR

答案:C 解析:FLOAT/DOUBLE 是二进制浮点,累加与比较会出现精度丢失;DECIMAL 按十进制精确存储,是金额与成绩的标准选择。详见《MySQL基础与SQL实战》

178.(MySQL·易)关于 DATETIME 与 TIMESTAMP,下列说法正确的是?

  • A. 两者都会随服务器时区的变化而改变存储的数值
  • B. TIMESTAMP 占 4 字节、存取时做时区转换,最大只能表示到 2038 年
  • C. DATETIME 占 4 字节,取值范围可到 9999 年
  • D. TIMESTAMP 与时区无关,DATETIME 反而受时区影响

答案:B 解析:DATETIME 占 8 字节、与时区无关、范围到 9999 年;需要跟随客户端时区变化的全球化场景才考虑 TIMESTAMP。详见《MySQL基础与SQL实战》

179.(MySQL·易)关于 COUNT(*)、COUNT(1)、COUNT(name),说法正确的是?

  • A. 三者完全等价,结果与性能都一样
  • B. COUNT(1) 只统计值为 1 的行,因此语义与 COUNT(*) 不同
  • C. COUNT(*) 会跳过 name 为 NULL 的行,另外两个不会
  • D. InnoDB 下 COUNT(*) 与 COUNT(1) 等价且官方有优化;COUNT(name) 不统计 name 为 NULL 的行

答案:D 解析:数字 1 只是占位,COUNT(1) 与 COUNT(*) 一样统计所有行;COUNT(列) 的语义才是”该列非 NULL 的行数”。COUNT(主键) 也并不更快。详见《MySQL基础与SQL实战》

180.(MySQL·易)关于 DROP TABLE、DELETE FROM t、TRUNCATE TABLE,说法正确的是?

  • A. 三者都在事务中可回滚
  • B. TRUNCATE 属 DDL,清空整表并重置自增,不可回滚
  • C. DELETE 不带 WHERE 时会自动重置自增
  • D. DROP 只删数据、保留表结构

答案:B 解析:DELETE 是 DML,逐行删、可带 WHERE、事务内可回滚、不重置自增;DROP 连表带结构一起删,最彻底。详见《MySQL基础与SQL实战》

181.(MySQL·易)库存表 stock(id BIGINT PRIMARY KEY, num INT NOT NULL) 中 id=1 的 num 为 1。“先 SELECT 查出来再判断再 UPDATE”在并发下会超卖,更稳妥的写法是?

  • A. 把隔离级别升到 SERIALIZABLE 就一定不会超卖
  • B. 先 SELECT num FROM stock WHERE id = 1;,应用层判断 num > 0 之后再 UPDATE stock SET num = 1 WHERE id = 1;
  • C. 用 UPDATE stock SET num = num - 1 WHERE id = 1 AND num > 0;,靠影响行数判断是否扣减成功
  • D. 把 num 声明成 INT NOT NULL 并给它加唯一索引

答案:C 解析:把判断与扣减合并成一条原子语句,由数据库保证原子性;影响行数为 0 即库存不足,回到应用层按失败处理。乐观锁版本号写法同理。详见《索引-事务-锁与调优》

182.(MySQL·中)表结构如下,执行 SELECT name FROM big_user WHERE name = 'user_999999'; 的执行计划最可能是什么?

  • A. type=ref,key=idx_name,Extra 含 Using index
  • B. type=ALL,key=NULL
  • C. type=range,key=PRIMARY,Extra 含 Using filesort
  • D. type=index,key=idx_name,Extra 含 Using temporary

答案:A 解析:等值条件命中 idx_name,且查询列 name 本身就在索引里,构成覆盖索引;二级索引自带主键,故 Extra 会显示 Using index。详见《索引-事务-锁与调优》

183.(MySQL·中)同样是 SELECT * FROM big_user WHERE name = 'user_999999';(表与索引同上一题),与上一题的写法相比,代价变大的主要原因是?

  • A. 二者没有实质差异,* 只是书写习惯
  • B. * 会让 idx_name 直接失效,必然退化成全表扫描
  • C. * 会让结果集自动去重,多出一次排序开销
  • D. 需要的列不在 idx_name 中,每行都要回表取整行,代价高于覆盖索引

答案:D 解析:二级索引只带主键,回表是随机 IO;当命中行数占比过高时,优化器甚至可能直接放弃索引改为全表扫描。详见《索引-事务-锁与调优》

184.(MySQL·中)已有联合索引 idx_city_age_created (city, age, created_at),下列哪条 SQL 能把该索引用得最充分?

  • A. SELECT * FROM t WHERE age = 20 ORDER BY created_at;
  • B. SELECT * FROM t WHERE city = '北京' AND created_at > '2026-01-01';
  • C. SELECT * FROM t WHERE city = '北京' AND age = 20 ORDER BY created_at;
  • D. SELECT * FROM t WHERE city = '北京' ORDER BY created_at;

答案:C 解析:C 三列连续命中,且索引顺序天然满足 ORDER BY,不产生 filesort;A 缺最左列,B 与 D 都跳过了 age,created_at 用不上。详见《索引-事务-锁与调优》

185.(MySQL·中)查询 SELECT * FROM t WHERE city = '北京' AND age = 20 ORDER BY created_at; 已建有 idx_city_age (city, age),explain 的 Extra 出现 Using filesort。最有效的改法是?

  • A. 把 created_at 追加到联合索引末尾,改建成 (city, age, created_at)
  • B. 给 created_at 单独建一个索引
  • C. 把 ORDER BY 改成 ORDER BY id
  • D. 调大 sort_buffer_size 参数

答案:A 解析:排序列紧跟在等值列之后,索引自身的顺序即满足 ORDER BY,filesort 随之消失;单列索引与本次排序无关。详见《索引-事务-锁与调优》

186.(MySQL·中)联合索引 idx_a_b (a, b),SQL 为 SELECT * FROM t WHERE a > 10 AND b = 5;,b 列的情况是?

  • A. b 仍能参与索引定位,等同于一次等值匹配
  • B. a 走了范围查询,b 无法再用于定位,只能靠索引下推在回表前过滤
  • C. 整条 SQL 用不上该索引,只能全表扫描
  • D. 优化器会自动把索引顺序改写成 (b, a) 后再使用

答案:B 解析:最左列遇到范围查询后,其后的列失去定位能力;MySQL 5.6+ 的索引下推能把 b=5 下推到引擎层过滤,减少回表次数。详见《索引-事务-锁与调优》

187.(MySQL·中)name 为 VARCHAR(20) 且有索引 idx_name,SQL SELECT * FROM t WHERE name = 13800000000; 的执行结果是?

  • A. 走 idx_name,type=ref,性能正常
  • B. 报语法错误,字符串列不能与数字比较
  • C. 走索引,但结果会漏掉部分行
  • D. 不走索引,字符串列与数字比较会触发隐式类型转换,相当于对索引列做了运算

答案:D 解析:此时 MySQL 会把列值转成数字再比较,索引的有序性被破坏;正确写法是加引号传字符串,让两边类型一致。详见《索引-事务-锁与调优》

188.(MySQL·中)表与数据如下,执行后面的 SQL,结果是?

  • A. 2 行:(1, 3, 255.5) 与 (2, 1, 74.0)
  • B. 1 行:(1, 3, 255.5)
  • C. 1 行:(1, 3, 78.0)
  • D. 报错,HAVING 不能引用 SELECT 中定义的别名 cnt

答案:B 解析:HAVING 执行在 SELECT 之后,可以引用 cnt、total 这类别名;学生 2 只有 1 条记录被滤掉,没有记录的学生也不会出现在结果里。详见《MySQL基础与SQL实战》

189.(MySQL·中)两阶段提交中,redo log、binlog 的写入先后顺序是?

  • A. redo log prepare → 写 binlog → redo log commit
  • B. 写 binlog → redo log prepare → redo log commit
  • C. redo log commit → 写 binlog → 写 undo log
  • D. 写 undo log → 写 binlog → redo log prepare

答案:A 解析:redo 先置 prepare、再写 binlog、最后置 commit;崩溃恢复时用 XID 去 binlog 中比对,据此决定提交还是回滚。详见《索引-事务-锁与调优》

190.(MySQL·中)关于插入意向锁(Insert Intention Lock),下列说法正确的是?

  • A. 它是表级锁,插入之前会先锁住整张表
  • B. 它本身就是间隙锁的一种,两个插入意向锁之间也互斥
  • C. 它是插入前设置的意向标记;间隙锁彼此不冲突,但它与范围内已存在的间隙锁冲突
  • D. 它只在 RC 隔离级别下才会产生

答案:C 解析:多个事务可同时持有同一间隙的间隙锁,一旦有人真要插入,插入意向锁便与这些间隙锁冲突而被阻塞——并发插入死锁的常见来源。详见《索引-事务-锁与调优》

191.(MySQL·中)RR 隔离级别下,两个会话按下列顺序执行(v 初值为 0),最终 t 表中 id=1 的 v 值是?

  • A. 1
  • B. 0
  • C. 报死锁错误,事务 A 被整体回滚
  • D. 2

答案:D 解析:A 的 SELECT 是快照读不阻塞 B;A 随后的 UPDATE 是当前读,会读到 B 已提交的最新值 1,再按自己的语义写为 2。详见《索引-事务-锁与调优》

192.(MySQL·中)订单表预计增长到 5 亿行,准备按 user_id 做水平分表。直接带来的后果是?

  • A. 表结构必须改成 MyISAM 才能分片
  • B. 主键不能再自增,必须改成无序 UUID
  • C. 分片键选了 user_id,按 order_id 查询无法直接定位分片,需要额外的路由或映射方案
  • D. 事务隔离级别必须降到 RC

答案:C 解析:按订单号查落不到确定分片,只能广播查询或再维护一张 order_id 到 user_id 的映射;分片后的全局唯一 ID 通常用雪花算法生成。详见《02-数据库与中间件面试速查》

193.(MySQL·难)事务在执行过程中尚未 COMMIT 时 MySQL 崩溃,重启后的处理是?

  • A. 扫描 redo log 中处于 prepare 状态的记录,用其中的 XID 去 binlog 比对:能找到就提交,找不到就回滚
  • B. 一律回滚,因为事务没有走到 commit 阶段
  • C. 一律提交,因为 redo log 已经持久化到磁盘
  • D. 由 undo log 的版本链自行决定,与 binlog 无关

答案:A 解析:binlog 写成功才是事务提交成功的标志;用 XID 回查 binlog,正是两阶段提交保证两份日志一致、进而保证主从一致的关键。详见《索引-事务-锁与调优》

194.(MySQL·难)主库执行 UPDATE account SET balance = balance - 100 WHERE id = 1;,redo log 已写入并处于 prepare,写 binlog 之前主库宕机且数据无法恢复。此时从库的情况是?

  • A. 从库的余额已经扣减 100,需要人工补偿
  • B. 从库没有这条变更,余额不变,与回滚后的主库保持一致
  • C. 从库会自动把这条事务补写进自己的 binlog
  • D. 从库进入只读模式,等待主库恢复后重新同步

答案:B 解析:binlog 没写成功,恢复时该事务回滚;从库只重放 binlog,自然也没有这条变更,两边同为”未扣款”。详见《索引-事务-锁与调优》

195.(MySQL·难)表 t(id BIGINT PRIMARY KEY, a INT, b INT) 上有 KEY idx_a (a),索引中 a 的值依次为 1、5、10、15。会话 A 执行:

  • A. UPDATE t SET b = 1 WHERE a = 10;
  • B. INSERT INTO t (id, a, b) VALUES (99, 8, 1);
  • C. SELECT * FROM t WHERE a = 10 FOR UPDATE;
  • D. SELECT b FROM t WHERE a = 10;

答案:D 解析:非唯一索引等值加锁会覆盖记录 10 及 (5,10]、(10,15] 等区间,A、C 撞上被锁记录,B 插入的 a=8 落在被锁间隙里;D 是快照读走 MVCC,不阻塞。详见《索引-事务-锁与调优》

196.(MySQL·难)关于覆盖索引与索引下推(ICP),下列说法正确的是?

  • A. 两者是同一件事的两种叫法
  • B. 覆盖索引指查询列全部落在索引中、无需回表;ICP 是把无法用于定位的索引列过滤下推到引擎层,减少回表次数
  • C. 使用覆盖索引时,explain 的 Extra 显示的是 Using index condition
  • D. ICP 默认关闭,需要手动打开才会生效

答案:B 解析:覆盖索引对应 Extra 的 Using index,ICP 对应 Using index condition;ICP 自 MySQL 5.6 起默认开启,两者优化的是不同环节。详见《索引-事务-锁与调优》

197.(MySQL·难)深分页优化中,延迟关联写法比原始写法快,原理是?

  • A. 它把一条 SQL 拆成两次查询并发执行
  • B. 它把排序工作挪到了应用层完成
  • C. 子查询只扫描较窄的二级索引即可拿到 10 个目标主键,避免为前 90 万行逐行取整行数据
  • D. 它命中了查询缓存,第二次直接从缓存读取

答案:C 解析:子查询在 idx_created 上完成排序与偏移,最后只对 10 个主键回表;游标分页(WHERE created_at > 上次值)则连偏移扫描都省掉,但只能顺序翻页。详见《索引-事务-锁与调优》

198.(Redis·易)Redis 的 Hash 类型在字段数较少、值较小时,采用哪种编码存储?

  • A. 跳表 + 哈希表
  • B. 整数集合(intset)
  • C. 压缩列表(ziplist/listpack)
  • D. 双向链表

答案:C 解析:小 Hash 用 ziplist/listpack 连续内存紧凑存放,字段数或值长度超过 hash-max-listpack-entries/value 阈值后才转成哈希表。intset 是 Set 在”元素全是整数且数量少”时的编码;跳表 + 哈希表是 ZSet 变大后的双结构。详见《01-Redis》

199.(Redis·易)下面哪一项最不适合用 Redis 的 List 来实现?

  • A. 按分数实时排名的游戏榜单
  • B. 取最新 10 条评论(LPUSH + LRANGE 截断)
  • C. 秒杀订单的异步落库队列(LPUSH + BRPOP)
  • D. 未读消息列表

答案:A 解析:List 只能按下标或头尾访问,没有按分数排序的能力,排行榜要交给 ZSet 的 score + ZREVRANGE。B/C/D 都是 List 的典型用法。详见《01-Redis》

200.(消息队列·易)RabbitMQ 中一条消息被消费失败且 basicNack 时 requeue 设为 false,它最终会去哪里?

  • A. 直接丢弃,没有任何记录
  • B. 自动重试 3 次后丢弃
  • C. 进入死信交换机,落到死信队列等人工或补偿任务处理
  • D. 退回给生产者重新发送

答案:C 解析:消息被 nack/reject 且不回队列、TTL 到期、队列超长这三种情况都会触发死信路由,前提是声明队列时绑定了死信交换机(x-dead-letter-exchange)。死信队列是排查”消息去哪了”的关键兜底,也常被用来配合 TTL 实现延迟队列。A 忽略了 DLX 的兜底作用;MQ 不会自动把消息退给生产者。详见《02-RabbitMQ》

201.(Redis·中)关于 Redis 的惰性删除,下列说法正确的是?

  • A. key 一到过期时间就被立即从内存中删除
  • B. key 过期后不主动回收,直到下一次访问到它时才检查并删除
  • C. 惰性删除意味着过期 key 永远不会被清理
  • D. 惰性删除是内存达到 maxmemory 之后才启动的机制

答案:B 解析:惰性删除把 CPU 开销摊到访问时刻,代价是”再也没人访问的过期 key 会一直占内存”,所以要配合定期删除(每 100ms 随机抽一批检查)。D 描述的是内存淘汰,是内存超限后的另一套机制。详见《01-Redis》

202.(Redis·易)纯缓存场景下,希望内存不足时优先淘汰最久未使用的数据,淘汰策略应配置为?

  • A. allkeys-lru
  • B. noeviction
  • C. volatile-ttl
  • D. allkeys-random

答案:A 解析:allkeys-lru 在全键范围内按最近最少使用淘汰,是缓存场景的常规选择。B 是默认值,内存满后写请求直接报错,适合把 Redis 当唯一数据源、宁可报错也不丢数据的场景;C 只在设了 TTL 的键里挑剩余寿命最短的;D 随机淘汰,命中率不可控。详见《01-Redis》

203.(Redis·中)下面哪种写法在 Redis Cluster 中容易触发 CROSSSLOT 错误?

  • A. GET 单个 key
  • B. 对单个 key 执行 INCR
  • C. 给单个 key 设置 EXPIRE
  • D. 对两个普通 key 直接做 SINTER(未使用 hash tag)

答案:D 解析:多 key 命令要求所有 key 落在同一个槽,user:1:follow 与 user:2:follow 大概率分属不同节点。解法是用 hash tag 强制同槽,例如 {u1}:follow 与 {u1}:fans。单 key 命令不受影响。详见《01-Redis》

204.(Redis·中)秒杀扣库存时用 Lua 脚本替代”先 GET 判断、再 DECR 扣减”两段代码,核心收益是?

  • A. Lua 脚本可以自动重试失败的命令
  • B. 脚本在 Redis 中作为一个整体串行执行,中间不会插入其他客户端的命令
  • C. Lua 脚本比 Java 代码更容易维护
  • D. 只有 Lua 脚本才能访问 Hash 类型

答案:B 解析:命令执行本身是单线程的,但两条命令之间仍可能插入其他请求,出现”判断时库存够、扣减时已被别人扣完”的超卖窗口。脚本整体执行消除了这个窗口。注意脚本内不要写耗时逻辑,否则会阻塞整个实例。详见《02-高并发系统设计》

205.(Redis·中)服务用本地 Caffeine + Redis 做多级缓存,商品价格修改后部分实例仍返回旧价格,最可能的原因是?

  • A. Caffeine 的容量设置得太小
  • B. Redis 不支持价格这类数值
  • C. 只删了 Redis 中的缓存,各实例进程内的 Caffeine 没有被通知失效
  • D. 商品价格这类数据必须直接查数据库

答案:C 解析:本地缓存不在 Redis 里,删 Redis 影响不到各实例自己的内存。做法是借助 Redis 发布订阅或键过期通知广播”某 key 失效”,各节点收到后清掉本地条目。这类广播丢一两次无大碍,但不能当可靠消息用。详见《01-Redis》

206.(Redis·中)主从 + 哨兵架构下,写完立即查询却读到旧值,最可能的原因是?

  • A. Redis 的 RDB 文件损坏
  • B. 复制是异步的,写落在主节点而读请求被路由到了尚未同步完成的从节点
  • C. 哨兵把写请求转发到了从节点
  • D. 从节点只读,所以读不到新数据

答案:B 解析:主节点写完本地就返回,命令再异步传播给从节点,这个窗口内的读从请求必然拿到旧值。对一致性敏感的操作(如支付后立刻查订单)要强制读主,或用 WAIT 等待若干副本确认。哨兵只负责监控与切换,不代理读写。详见《01-Redis》

207.(Redis·难)一个占用 60GB 内存的 Redis 实例执行 bgsave,关于 fork 与写时复制,下列说法正确的是?

  • A. fork 会立即复制一份 60GB 内存给子进程,实例随即 OOM
  • B. fork 会挂起父进程直到快照完成,期间所有请求阻塞
  • C. fork 瞬间只复制页表、开销很小,但快照期间父进程的写入会触发页复制,内存可能接近翻倍
  • D. 内存越大 fork 越快,60GB 实例几乎无感知

答案:C 解析:Linux 的 fork 采用写时复制,并非立刻复制 60GB,所以 A 错;但快照期间父进程每修改一个内存页就要复制一份出来,最坏情况内存接近翻倍,叠加内存碎片后容易 OOM,因此要预留一倍余量并控制 maxmemory。fork 复制页表的耗时随内存量增长,大实例会有毫秒级停顿,B 的”挂起直到完成”夸大了。详见《01-Redis》

208.(Redis·难)AOF 重写期间客户端仍在不停写入,Redis 靠什么保证这批新命令不丢?

  • A. 重写期间直接拒绝所有新的写命令
  • B. 新命令只进内存,重写结束后由客户端重新发送
  • C. 重写期间临时关闭 AOF,结束后再打开
  • D. 新命令同时追加到旧 AOF 和重写缓冲区,新文件写完后补上这段增量再原子替换

答案:D 解析:重写子进程只持有 fork 那一刻的数据视图,感知不到后续写入,所以父进程把新命令额外积累到 aof_rewrite_buf;新 AOF 落盘后先追加这段增量、再改名替换旧文件,切换期间几乎没有数据缺口。代价是这份缓冲区同样吃内存,写流量极大时需留意。详见《01-Redis》

209.(Redis·中)热点 key 失效后的重建,用”逻辑过期”与”互斥锁”最本质的差别是?

  • A. 逻辑过期让请求直接拿到旧值再异步重建,互斥锁让其余请求阻塞等待重建结果
  • B. 逻辑过期不会产生任何数据不一致
  • C. 互斥锁方案不需要考虑锁超时问题
  • D. 两者完全等价,只是代码写法不同

答案:A 解析:逻辑过期把过期时间写进 value、key 本身不设 TTL,命中后发现逻辑过期就返回旧值并异步刷新,吞吐高但会短暂返回旧数据;互斥锁只放一个线程回源、其余等待,一致性好但要处理锁超时、吞吐较低。选哪种取决于业务能否容忍短时旧值;两者都不解决”数据根本不存在”的穿透问题。详见《01-Redis》

210.(Redis·难)在 Cache Aside 之上再叠加”延迟双删”,是为了堵住哪个时序漏洞?

  • A. 数据库主从延迟导致从库读不到最新数据
  • B. 删除缓存这个动作本身不是原子的
  • C. Redis 的内存淘汰策略会误删缓存
  • D. 读请求在写请求删除缓存之后、更新数据库之前查到旧值,随后又把旧值回填进缓存

答案:D 解析:典型时序是:读线程查库拿到旧值 → 写线程更新数据库并删缓存 → 读线程此时才把旧值写回缓存,脏数据就长期留下了。延迟双删做成”删缓存 → 更新数据库 → 延迟 500ms~1s 再删一次”,清掉这次延迟加载造成的脏数据。它依赖对延迟时长的估计、并不保险,更彻底的做法是订阅 binlog 异步删缓存。详见《01-Redis》

211.(消息队列·易)团队要做日志采集与用户行为埋点,数据量极大,下游既有实时计算任务,也需要能回溯重放历史数据。按常规选型结论,最合适的是?

  • A. RabbitMQ:延迟最低、路由最灵活,适合海量日志
  • B. Kafka:吞吐最高,且消息按保留策略留存、消费只移动 offset,天然支持回溯重放
  • C. RocketMQ:事务消息最成熟,日志场景首选
  • D. 三者都可以,日志场景对吞吐和回放都没有要求

答案:B 解析:选型看三个维度:吞吐(Kafka 百万级 > RocketMQ 十万级 > RabbitMQ 万~十万级)、功能(业务功能 RocketMQ 最全,路由能力 RabbitMQ 最强)、数据保留方式。日志/埋点/大数据管道这类”量大、要多方消费、要能重放”的场景与 Kafka 的日志化模型天然契合。RabbitMQ 的优势在低延迟与灵活路由,但吞吐量级和”确认即删”的模型都不匹配。详见《04-RocketMQ》

212.(消息队列·中)关于 Kafka 的 rebalance(分区再平衡),下列说法正确的是?

  • A. 只有消费者数量变化才会触发,生产者增加分区数不会
  • B. 频繁 rebalance 只影响新加入的消费者,组内其他消费者不受影响
  • C. 成员增减、订阅的 topic 变化、分区数变化都会触发;Sticky/CooperativeSticky 策略让分区尽量留在原消费者上、只做增量调整,减少整组停顿
  • D. rebalance 期间消费照常进行,只是分区归属发生改变

答案:C 解析:触发条件有三类——组内成员增减(上下线、宕机)、分区数变化、订阅的 topic 变化,A 漏了后两类。rebalance 期间整个消费组停止消费(类似 STW),所以 B、D 都错:影响的是全组,且停摆会直接造成消费延迟。分区分配策略中 Range/RoundRobin 属于全量重分配,Sticky 与 CooperativeSticky 尽量保留原有归属、只做增量调整,其中 CooperativeSticky 支持增量式协作重平衡,停顿最短。工程上要避免频繁 rebalance:合理设置 session.timeout.ms,并确保消费耗时不超过 max.poll.interval.ms。详见《03-Kafka》

213.(消息队列·易)希望 order.* 能匹配 order.create、但不匹配 order.create.success,应选择哪类交换机?

  • A. direct
  • B. topic
  • C. fanout
  • D. headers

答案:B 解析:topic 的通配符中 * 匹配一级、# 匹配多级,order.# 才会命中 order.create.success。direct 只做精确匹配;fanout 忽略 RoutingKey 广播给所有绑定队列;headers 按消息头匹配,性能差、很少用。详见《02-RabbitMQ》

214.(消息队列·易)Kafka 能支持消息回溯、被多个消费组反复消费,根本原因是?

  • A. 每条消息带有全局唯一的 UUID
  • B. 消费者本地缓存了全量消息副本
  • C. Kafka 会把同一条消息推送给所有消费组
  • D. 消息按保留策略存在日志文件里,消费只是移动 offset,并不删除消息

答案:D 解析:Kafka 把消息当作可追加的日志,消费进度只是一个位移数字,重置 offset 就能重放历史数据。这与 RabbitMQ”确认即删除”的模型形成对照,也是它适合日志采集与大数据管道的原因。详见《03-Kafka》

215.(消息队列·中)RocketMQ 的延迟消息,相比 RabbitMQ 用 TTL + 死信实现延迟,主要优势在于?

  • A. Broker 侧按到期时间调度投递,不受队列先进先出顺序的限制
  • B. 延迟精度可以做到纳秒级
  • C. 延迟消息不占用磁盘空间
  • D. RabbitMQ 的 TTL 方案完全无法实现延迟效果

答案:A 解析:TTL + 死信方案里队列是先进先出的:排在前面的消息还没到期,后面已经到期的也出不去,这就是队头阻塞(同一队列内各条消息延迟时长不一致时尤其明显)。RocketMQ 先把消息存进内部调度 Topic,到期后再投回真实 Topic,因此没有这个问题(4.x 为 18 个固定等级,5.x 用时间轮支持任意时刻)。详见《04-RocketMQ》

216.(Redis·难)Redis Cluster 中,客户端向一个并不负责该 key 的节点发起请求,关于节点的响应,下列说法正确的是?

  • A. 节点静默把请求转发给正确的节点,客户端完全无感知
  • B. 节点返回 MOVED 错误并带上目标节点地址,由客户端更新槽映射后重新发起请求
  • C. 节点返回 nil,客户端只能自己遍历所有节点去找数据
  • D. 节点会自动把这个 key 迁移到自己这里,之后由本节点负责

答案:B 解析:槽的归属是预先划分好的,节点不做代理转发,而是用 MOVED 告诉客户端”这个槽现在归谁”,智能客户端据此刷新本地槽映射再重试——所以叫重定向而不是转发。D 说反了:key 归哪个节点由它落在哪个槽决定,节点不能自行把槽搬过来。补充一个高频追问:如果该槽正在迁移中,节点会返回 ASK,让客户端仅对这一次请求临时改投目标节点,且不更新客户端的槽映射——MOVED 是永久改派、ASK 是一次性绕行,这是两者的关键区别。详见《01-Redis》

217.(消息队列·中)Kafka 生产者设置 acks=all 时,为什么还建议搭配 min.insync.replicas=2?

  • A. 让消息被包括未同步副本在内的所有副本确认
  • B. 为每条消息自动开启事务
  • C. 让 ISR 收缩到只剩 Leader 时仍能写入成功
  • D. acks=all 只要求所有 ISR 成员确认,而 ISR 会收缩;设成 2 后同步副本不足时写入直接失败,避免退化成”只剩 Leader 也算 all”

答案:D 解析:ISR 是动态集合,落后太多的副本会被踢出,极端情况下只剩 Leader,此时 acks=all 等价于 acks=1。min.insync.replicas=2 把”至少两个副本同步成功”变成写入的前置条件,不足则抛 NotEnoughReplicas,可靠性与预期才对得上。A 错在未同步的副本本就不属于 ISR。详见《03-Kafka》

218.(消息队列·中)Kafka 消费者保留默认的 enable.auto.commit=true,可能在什么情况下丢消息?

  • A. 位移按固定间隔自动提交,业务还没处理完就宕机,重启后从已提交位移继续,中间那批消息被跳过
  • B. 自动提交会让 Broker 拒绝投递新消息
  • C. 自动提交会覆盖生产者的 acks 配置
  • D. 自动提交只能提交偶数位移,造成错位

答案:A 解析:自动提交是定时任务,提交的位移与业务处理进度完全脱钩,正是”先提交后处理”的典型丢消息模式。业务系统一律关闭自动提交、改为处理完成后手动提交;代价是宕机后会重复消费,所以还需要消费端幂等。详见《03-Kafka》

219.(消息队列·中)Kafka 消费组里消费者数量超过了 Topic 的分区数,会发生什么?

  • A. 多出来的消费者会自动拆分现有分区继续消费,总吞吐继续线性提升
  • B. 消费组会拒绝新成员加入并报错
  • C. 每个分区会被两个消费者同时消费,消费速度翻倍
  • D. 多出来的消费者分不到分区、处于空闲状态;想提升并行度只能先增加分区数

答案:D 解析:分区是并行消费的最小单位,组内一个分区同一时刻只能归一个消费者,因此并行度上限就是分区数。这也是消息积压时”盲目加消费者”无效的原因——正确顺序是先扩分区(或新建多分区 Topic 搬迁数据),再扩消费者。详见《03-Kafka》

220.(消息队列·难)生产端已配置 acks=all 与重试,但代码里 kafkaTemplate.send() 之后从不检查返回值,消息仍可能丢失,原因是?

  • A. acks=all 在消息小于 1KB 时会自动降级为 acks=1
  • B. 只要配了 acks=all,任何情况下都不会丢
  • C. send() 是异步的,失败异常被吞在返回的 future 里,不阻塞等待或注册回调就完全无感知
  • D. KafkaTemplate 会把失败的消息自动写入死信队列

答案:C 解析:可靠性配置只管”发出去之后”的链路,管不了”根本没发出去”。高可靠场景必须显式检查发送结果,失败落库补偿或重试。死信队列是消费侧的机制,生产侧没有自动兜底。详见《03-Kafka》

221.(消息队列·难)RocketMQ 事务消息的”事务回查”机制,主要解决什么问题?

  • A. 生产者执行完本地事务后,Commit/Rollback 丢失或生产者宕机,半消息状态悬而未决
  • B. 消费者重复消费的问题
  • C. Broker 磁盘写满导致的写入失败
  • D. 消息在 MessageQueue 中的顺序错乱

答案:A 解析:正常流程下生产者会在本地事务结束后主动二次确认,但网络抖动或进程崩溃可能让这次确认永远到不了 Broker,半消息就一直悬着(对消费者不可见)。回查让 Broker 定时反查生产者”这笔本地事务到底成了没有”,据此决定提交或回滚。注意事务消息只保证生产端与本地事务一致,消费端仍需幂等。详见《04-RocketMQ》

222.(Elasticsearch·易)要对 text 类型字段 name 做「整句精确匹配」查询,正确的写法是?

  • A. 用 term 查询 name.keyword 子字段(mapping 中该 text 字段配了 keyword 子字段)
  • B. 用 match 查询 name 字段
  • C. 用 match_phrase 查询 name 字段
  • D. 用 term 查询 name 字段本身

答案:A 解析:text 字段写入时已被分词,倒排表里根本不存在「整句」这个词,所以直接 term 查 name 通常查不到(选项 D 是经典错误);B、C 都属于会对查询词再分词的全文检索。给 text 字段加 keyword 子字段后,term 查这个不分词的子字段才能精确命中。详见《05-Elasticsearch》

223.(Elasticsearch·中)把价格区间过滤放进 bool 的 filter 子句而不是 must 子句,主要好处是?

  • A. filter 支持 range 查询而 must 不支持
  • B. filter 会提升文档的 BM25 相关度得分
  • C. filter 子句之间是 OR 关系,能召回更多文档
  • D. filter 不参与相关性打分且过滤结果可被缓存,纯过滤场景性能更好

答案:D 解析:must 参与 BM25 打分计算,filter 只判断「命中与否」不算分,并且过滤结果可以缓存成 bitset 复用。所以状态、时间、价格区间这类只影响「筛选」不影响「排序」的条件应放 filter,只有真正影响相关度的全文条件才放 must。详见《05-Elasticsearch》

224.(Elasticsearch·难)关于 Elasticsearch 的 refresh 与 flush,下列说法正确的是?

  • A. refresh 负责把 segment 持久化到磁盘并清空 translog
  • B. flush 负责让新写入的文档变得可被搜索
  • C. refresh(默认 1s)把内存缓冲中的文档生成 segment 并写入 OS 页缓存,此时文档才可被搜索;flush(默认 30 分钟或 translog 写满)才把 segment 落盘并清空 translog
  • D. 两者默认都每 1 秒执行一次,且一旦关闭 refresh 就会丢数据

答案:C 解析:写入先进入内存 buffer 与 translog(防丢),refresh 后才可被搜索——这正是「近实时」的来源;flush 才是真正的持久化与 translog 清理。关闭 refresh 只影响可见性、不会丢数据,因为 translog 仍在记录。详见《05-Elasticsearch》

225.(Elasticsearch·难)商品搜索要做「无限下拉」式深翻页(可能翻到几万条之后),最合适的方案是?

  • A. 不断加大 from 的值,配合 size=20 逐页取
  • B. 用 search_after 配合 PIT,以上一页最后一条的排序值作为游标继续取下一页
  • C. 用 scroll 为每个在线用户维持一个游标来翻页
  • D. 在每个分片上各取前 N 条,再在客户端归并去重

答案:B 解析:from+size 需要每个分片都取出 from+size 条到协调节点归并排序,代价随 from 线性增长且默认上限 10000;scroll 会长时间占用资源维持快照,适合一次性全量导出而非用户翻页;search_after 靠游标定位,代价与页深无关,配合 PIT 还能保证翻页期间数据视图一致。详见《05-Elasticsearch》

226.(Elasticsearch·中)某索引现有 6 个主分片已接近单分片容量上限,正确的扩容做法是?

  • A. 新建一个分片数更多的索引,用 _reindex 把数据导过去,再用别名切换,业务无感
  • B. 调用 _settings 接口把 number_of_shards 从 6 改成 12
  • C. 把副本数从 1 调到 3,写入容量随之翻倍
  • D. 重启集群,主分片会自动分裂成更多份

答案:A 解析:主分片数在建索引时固定、之后不可修改,因为文档路由公式依赖它,改分片数等于让所有历史数据「找错门」,只能重建索引后切别名。副本分片只提升读吞吐与容灾能力,并不分担写入。详见《05-Elasticsearch》

227.(Elasticsearch·易)中文商品搜索中,analyzer 与 search_analyzer 的常见搭配是?

  • A. 两个都用 standard,中文会被切成单字
  • B. analyzer 用 ik_smart,search_analyzer 用 ik_max_word
  • C. 只设置 search_analyzer,analyzer 保持默认
  • D. analyzer 用 ik_max_word 保证写入侧召回,search_analyzer 用 ik_smart 让查询词更精炼

答案:D 解析:写入用最细粒度切词,保证任何合理查询都能命中;查询用粗粒度切词,减少无关词带来的噪声,两者配合才能兼顾召回与精度。standard 对中文基本逐字切分,噪声大、相关性差,中文检索必须换 IK 插件。详见《05-Elasticsearch》

228.(Elasticsearch·易)执行 GET /_cluster/health 返回 status 为 yellow,说明?

  • A. 主分片已全部分配,但存在副本分片未分配(单节点集群几乎必然如此)
  • B. 主分片与副本分片都已正常分配,集群完全健康
  • C. 至少有一个主分片未分配,这部分数据已经查不到
  • D. 集群正在做段合并,属于正常的中间状态

答案:A 解析:green 表示主副分片全部就绪;yellow 表示主分片齐全但副本无处安放(只有一台节点时没有别处可放副本,所以单节点集群长期是 yellow);red 才是有主分片未分配、部分数据不可用。测试环境看到 yellow 不必惊慌。详见《05-Elasticsearch》

229.(ZooKeeper·中)在 zkCli 中执行 create -e /order-svc "192.168.1.10:8080" 后 quit 退出,重新进入 zkCli 执行 ls /order-svc 会看到什么?

  • A. 节点仍存在,数据保持原样
  • B. 提示节点不存在——-e 创建的是临时节点,随会话断开被自动删除
  • C. 节点还在,但数据被清空
  • D. 节点自动转换成了持久节点

答案:B 解析:-e 即 ephemeral,生命周期绑定客户端会话,会话断开即消失——这正是服务注册能「实例宕机自动摘除」的原理。不加 -e 时默认创建持久节点,会话断开也不会消失。详见《06-ZooKeeper与Nacos》

230.(ZooKeeper·难)用 ZooKeeper 实现分布式锁时,如果所有等待者都监听同一个锁节点,锁释放瞬间会出现什么现象?

  • A. 只唤醒排队时间最久的那一个客户端
  • B. ZooKeeper 会把通知自动排队,按注册先后逐个唤醒
  • C. 所有等待者同时被唤醒并涌向服务端抢锁,形成羊群效应,给 ZK 带来瞬时压力
  • D. Watcher 只触发一次,所以其余客户端永远收不到通知

答案:C 解析:这就是「惊群/羊群效应」:一次事件唤醒大量客户端,最终只有一个抢到锁,其余白跑一趟。正确做法是改用临时顺序节点,每个客户端只监听「比自己序号小的前一个节点」,释放时只唤醒下一个等待者。详见《06-ZooKeeper与Nacos》

231.(ZooKeeper·难)关于 ZAB 与 Paxos 的关系,说法正确的是?

  • A. Paxos 是通用的分布式一致性算法;ZAB 是为「主备模式的原子广播」设计的协议,靠 zxid/epoch 保证全局有序,包含消息广播与崩溃恢复两个阶段
  • B. ZAB 是通用一致性算法,而 Paxos 只能用于主备系统
  • C. 两者完全等价,ZAB 只是 Paxos 的 Java 语言实现
  • D. ZAB 只要 Leader 自己写入成功即可提交,无需过半确认

答案:A 解析:Paxos 解决「一批节点就某个值达成一致」的通用问题,本身不保证全序;ZAB 面向主备复制状态机,要求所有写请求经 Leader 排序后广播、过半节点确认才提交,从而保证各副本状态顺序一致。详见《06-ZooKeeper与Nacos》

232.(ZooKeeper·中)「ZooKeeper 不适合做注册中心」最主要的理由是?

  • A. ZK 不支持临时节点,无法自动摘除宕机实例
  • B. ZK 没有 Watcher 机制,调用方无法感知实例列表变化
  • C. ZK 只能单机部署,无法组建集群
  • D. ZK 是 CP 系统,Leader 选举期间整个集群拒绝服务,而服务发现更看重可用性——宁可返回一份略旧的实例列表

答案:D 解析:A、B、C 都与事实相反,临时节点与 Watcher 恰恰是 ZK 的看家本领。真正的短板在 CP 语义带来的「选举期不可写」,再加上 Watcher 一次性、无控制台、运维成本高,新项目基本不再选它做注册中心。详见《06-ZooKeeper与Nacos》

233.(ZooKeeper·中)Nacos 的「保护阈值」是为了解决什么问题?

  • A. 限制单个客户端对配置中心的请求频率
  • B. 当健康实例比例低于阈值时,把不健康实例也一并返回给调用方,避免返回空列表导致整个服务不可用
  • C. 当配置中心连接断开时自动降级读取本地缓存
  • D. 心跳超过 15 秒未到达就立即剔除该实例

答案:B 解析:这是「可用性优先」的体现:返回一份可能部分不可用的列表,调用方最多部分请求失败;一旦返回空列表,整条调用链会瞬间全挂。D 描述的是心跳超时标记不健康的机制(15 秒),并非保护阈值。详见《06-ZooKeeper与Nacos》

234.(ZooKeeper·中)Nacos 中 Distro 协议与 Raft 协议的分工,正确的是?

  • A. 临时实例走 Distro(AP,异步复制、最终一致),持久实例与配置中心走 Raft(CP,强一致)
  • B. 临时实例走 Raft 保强一致,持久实例走 Distro 保可用
  • C. 两者都走 Raft,只是配置参数不同
  • D. Distro 负责配置存储,Raft 负责服务发现

答案:A 解析:绝大多数微服务注册成临时实例(默认),要的正是「宕机也能返回旧列表」的 AP 语义,靠客户端心跳与超时剔除实现最终一致;数据库类等必须全员强一致的服务才注册成持久实例走 CP。同一集群可同时承载两种模式。详见《06-ZooKeeper与Nacos》

235.(Nginx·中)配置 location /api/ { proxy_pass http://backend/; },浏览器请求 GET /api/user/1 时后端收到的路径是?

  • A. /api/user/1
  • B. /user/1
  • C. /backend/api/user/1
  • D. /api/

答案:B 解析:proxy_pass 的地址结尾带 / 时,Nginx 会把 location 匹配到的前缀 /api/ 替换掉再转发;若写成 proxy_pass http://backend;(结尾不带 /),则会把 /api/user/1 原样转发给后端。这一字之差是配置反向代理最常见的坑。详见《07-Nginx》

236.(Nginx·中)limit_req zone=apiLimit burst=20 nodelay; 中 nodelay 的作用是?

  • A. 超过 burst 的请求直接返回 503,不做任何缓冲
  • B. 把限流速率 rate 降到原来的一半
  • C. burst 范围内的突发请求立即被处理,不按 rate 匀速排队等待
  • D. 关闭限流计数,只记录日志不拦截

答案:C 解析:burst 是允许排队的突发量;不加 nodelay 时这些请求会被匀速放行,客户端看到明显延迟;加了 nodelay 则立即处理,只有超出 burst 的部分才会被拒绝。rate=10r/s 约束的是平均速率,burst 让瞬时尖峰能被吸收。详见《07-Nginx》

237.(Nginx·难)多个 worker 进程同时监听同一端口会引发惊群问题,Nginx 的应对方式是?

  • A. 把 worker_processes 设为 1,单进程自然没有惊群
  • B. 用 accept_mutex 让 worker 串行竞争 accept 权,同一时刻只让一个 worker 监听新连接(新内核下由 EPOLLEXCLUSIVE 承担同类职责)
  • C. 关闭 epoll,改用 select 模型
  • D. 让每个 worker 监听不同端口,再由 master 统一转发

答案:B 解析:惊群指新连接到来时,所有阻塞在事件等待上的 worker 都被唤醒,却只有一个能 accept,其余白白被唤醒消耗 CPU。accept_mutex 通过「同一时刻只有一个 worker 有机会 accept」消除无谓唤醒;worker 数通常设为 CPU 核数,多进程 + epoll 正是 Nginx 高并发的根基。详见《07-Nginx》

238.(Nginx·易)Vue history 模式下刷新子路由出现 404,应该补上的 Nginx 配置是?

  • A. location / { return 301 /index.html; }
  • B. location / { proxy_pass http://backend; }
  • C. location / { expires 7d; }
  • D. location / { root /usr/share/nginx/html; try_files $uri $uri/ /index.html; }

答案:D 解析:try_files 依次尝试「文件 → 目录 → 兜底 index.html」,把找不到的路径交给前端路由自己接管。A 用 301 会改写浏览器地址栏,B 会把静态资源请求打到后端应用,C 只管缓存头、解决不了 404。详见《07-Nginx》

239.(MongoDB·易)MongoDB 默认生成的 ObjectId 由哪几部分组成?

  • A. 4 字节时间戳 + 8 字节纯随机数
  • B. 4 字节秒级时间戳 + 5 字节机器/进程随机值 + 3 字节自增计数
  • C. 12 字节整型自增序列
  • D. 8 字节时间戳 + 4 字节分片键哈希

答案:B 解析:共 12 字节,前 4 字节是创建时间(所以 ObjectId 大致按时间递增),中间 5 字节标识机器与进程,最后 3 字节是同一秒内的自增计数。应用端即可生成、不依赖数据库协调,天然趋势递增,适合做索引键。详见《08-MongoDB》

240.(MongoDB·中)要按品牌统计商品数量与均价(只看 1000 元以上的商品),聚合管道的阶段顺序应为?

  • A. [{ $match: { price: { $gte: 1000 } } }, { $group: { _id: “$brand”, count: { $sum: 1 }, avgPrice: { $avg: “$price” } } }, { $sort: { count: -1 } }]
  • B. [{ $group: { _id: “$brand”, count: { $sum: 1 } } }, { $match: { price: { $gte: 1000 } } }, { $sort: { count: -1 } }]
  • C. [{ $sort: { count: -1 } }, { $match: { price: { $gte: 1000 } } }, { $group: { _id: “$brand” } }]
  • D. [{ $group: { _id: “$brand”, count: { $sum: 1 } } }, { $sort: { count: -1 } }, { $match: { price: { $gte: 1000 } } }]

答案:A 解析:聚合管道严格按数组顺序执行。$match 越靠前越能先滤掉无关文档、大幅减少后续阶段的数据量(还能利用索引),$group 做分组聚合,$sort 最后对聚合结果排序。B、D 在 $group 之后才过滤,等于白算了一大批文档;C 里 $sort 的 count 字段此时根本不存在。详见《08-MongoDB》

241.(MongoDB·难)关于 MongoDB 的写关注(writeConcern),说法正确的是?

  • A. w:1 表示所有副本节点都确认后才返回
  • B. w:0 表示必须等主节点落盘后才返回
  • C. w:“majority” 表示多数节点确认后才返回,可降低主节点故障回滚导致的数据丢失风险;j:true 则要求写入 journal 日志后才确认
  • D. writeConcern 只对分片集群生效,副本集下没有意义

答案:C 解析:w 决定「多少个节点确认才返回」:w:0 是发出去就不管(最快,可能丢),w:1 只需主节点确认(主节点故障时可能被回滚),w:“majority” 最稳但延迟最高;j 控制是否等待写入 journal 落盘。写关注越高越安全、越慢,需按业务取舍。详见《08-MongoDB》

242.(框架·中)某事务方法内部执行 try { mapper.update(x); } catch (Exception e) { log.error("更新失败", e); },异常被捕获后方法正常返回,这条 update 会?

  • A. 因为事务内出现过异常,仍然回滚
  • B. 方法返回后 Spring 会复查日志,发现异常后回滚
  • C. 不回滚:代理感知不到异常,事务正常提交
  • D. 取决于 update 与后续操作是否在同一个 Mapper

答案:C 解析:回滚与否只看异常有没有传播到代理;被 try-catch 吞掉后代理认为方法正常完成,于是提交已执行的更新——这就是”异常被吞导致事务失效”。要局部容错又想回滚,需显式标记回滚,或把该操作拆到另一个 Bean 的独立事务里。详见《Spring核心》

243.(框架·中)@Bean public OrderService orderService(UserRepository repo) { return new OrderService(repo); } 这种写法?

  • A. 可行:容器会解析方法参数并从容器中按类型注入
  • B. 编译报错:@Bean 方法的签名不允许带参数
  • C. 启动报错:@Bean 方法只能返回容器已有的 Bean
  • D. 可以启动,但参数恒为 null,仍需在方法体里手动 new

答案:A 解析:@Bean 方法的参数由 Spring 自动解析注入,等价于构造器注入;@Configuration + @Bean 适合装配没有源码、打不了注解的第三方类。详见《Spring核心》

244.(框架·难)Spring 中的 AOP 代理对象,通常是在 Bean 生命周期的哪个环节由 BeanPostProcessor 生成的?

  • A. 实例化之前,由 InstantiationAwareBeanPostProcessor 预先生成
  • B. 属性填充阶段,与依赖注入同时完成
  • C. postProcessAfterInitialization:初始化方法都执行完之后
  • D. postProcessBeforeInitialization:@PostConstruct 执行之前

答案:C 解析:初始化链路是 Aware 回调 → postProcessBeforeInitialization → @PostConstruct/afterPropertiesSet/init-method → postProcessAfterInitialization,代理通常在最后一步由 AbstractAutoProxyCreator 生成,因此代理对象里的目标对象是”完全初始化好的 Bean”。详见《Spring核心》

245.(框架·中)在一个 @Transactional(propagation = REQUIRED) 的 orderCreate 方法中,跨 Bean 调用了另一个 @Transactional(propagation = REQUIRES_NEW) 的 saveLog 方法;saveLog 执行成功后 orderCreate 抛异常,结果是?

  • A. 订单更新与日志一起回滚
  • B. 订单更新与日志一起提交
  • C. 日志回滚,订单更新提交
  • D. 订单更新回滚,日志已经独立提交、不随之回滚

答案:D 解析:REQUIRED 会加入当前事务,REQUIRES_NEW 则挂起当前事务另开一个并先行提交,外层回滚影响不到它——这正是”业务失败但操作日志必须留痕”的做法。注意 NESTED 相反:外层回滚时它一定回滚。详见《Spring核心》

246.(框架·中)自动配置类中的 @Bean 方法标注了 @ConditionalOnMissingBean,而用户自己定义了一个同类型 Bean,结果是?

  • A. 两个 Bean 都注册,注入时抛 NoUniqueBeanDefinitionException
  • B. 用户定义的 Bean 被覆盖,自动配置的生效
  • C. 自动配置的 Bean 不注册,用户定义的生效
  • D. 必须再加 @Primary 才能决定用哪一个

答案:C 解析:@ConditionalOnMissingBean 的含义是”容器里没有同类型 Bean 才生效”,所以用户的配置永远让位——这就是”默认配置永远让位给你的配置”,也是约定优于配置的实现方式。详见《SpringBoot实战》

247.(框架·易)下列哪个注解不属于 @SpringBootApplication 的组成部分?

  • A. @SpringBootConfiguration
  • B. @EnableAutoConfiguration
  • C. @ComponentScan
  • D. @MapperScan

答案:D 解析:@SpringBootApplication 是三合一:@SpringBootConfiguration(本质是 @Configuration)、@EnableAutoConfiguration(自动配置开关)、@ComponentScan(扫描主类所在包及子包)。@MapperScan 是 MyBatis 扫描 Mapper 接口用的,需要自己加。详见《SpringBoot实战》

248.(框架·中)public interface TodoMapper extends BaseMapper<Todo> {} 是一个完全空的接口,关于它的说法正确的是?

  • A. 必须再手写一个实现类,否则启动就报错
  • B. 只要起了这个名字,BaseMapper 就会为它生成多表关联查询
  • C. 不写 XML 就无法进行任何数据库操作
  • D. 继承即得单表 CRUD,无需实现类;多表复杂 SQL 仍要回到 XML 手写

答案:D 解析:MyBatis-Plus 的定位是消灭单表 CRUD 的重复劳动,Condition 构造器还能省掉大量动态 SQL;但复杂关联查询仍推荐手写 XML——知道这条边界比背 API 重要。详见《MyBatis》

249.(框架·中)只传 title 参数执行下面这段 update,<set> 标签起到的作用是?

  • A. 把 set 关键字替换成 update,兼容不同数据库
  • B. 参数全为空时生成 SET WHERE,需要自己兜底
  • C. 只保留第一个生效的 if 内容,其余丢弃
  • D. 自动去掉末尾多余的逗号,生成 UPDATE todo SET title = ? WHERE id = ?

答案:D 解析:<set> 会智能追加 SET 关键字并删除末尾多余的逗号;与之对应 <where> 自动去掉开头的 AND/OR,两者底层都由 <trim> 实现。记忆口诀:if 拼接、where 去头、set 去尾。详见《MyBatis》

250.(框架·难)MyBatis 二级缓存(namespace 级)在生产环境通常不开启,主要原因是?

  • A. 二级缓存默认关闭,必须改造源码才能开启
  • B. 二级缓存只在单个 SqlSession 内生效,命中率太低
  • C. 其他 Mapper 更新了关联表时本 namespace 的缓存不会被清,容易读到旧数据
  • D. 二级缓存的 key 只有 SQL 语句,与 namespace 无关,会互相覆盖

答案:C 解析:二级缓存按 namespace 隔离、跨 SqlSession 共享,多表关联时任一张表被别的 Mapper 更新,本 namespace 感知不到,于是脏读;分布式部署下多节点缓存也不同步。生产一般关闭它,统一用 Redis 做缓存。详见《MyBatis》

251.(框架·难)把 mybatis-plus.configuration.map-underscore-to-camel-case 显式设为 false,实体属性是 LocalDateTime createdAt、数据库列是 created_at,执行 selectById(没写 resultMap,SQL 也没起别名)的结果是?

  • A. 正常映射:框架会自动把 created_at 改写成驼峰列名
  • B. createdAt 为 null:列名与属性名对不上,自动映射失败
  • C. 启动直接报错,提示列名与属性名不匹配
  • D. 查询抛异常,必须给该字段加 @TableField 才能启动

答案:B 解析:下划线转驼峰是自动映射的一部分,关掉之后 created_at 找不到同名属性,字段就一直是 null,而程序不会报错——这类”静默丢字段”最难排查。生产建议保持开启,或在 SQL 里用别名 / resultMap 显式映射。详见《MyBatis》

252.(框架·易)Gateway 路由配置里 uri: lb://order-service 中的 lb 表示?

  • A. 从注册中心获取该服务的实例列表,负载均衡选一台发请求
  • B. 直接按 yml 里写死的 IP 直连,不做负载均衡
  • C. 只在本地开发环境生效,生产必须换成 http://
  • D. 表示对这条路由使用长连接,减少握手开销

答案:A 解析:lb 即 load balance:网关拿着服务名去注册中心取实例列表,再由负载均衡器选一台转发。写成 http://ip:port 就退化成写死地址,扩缩容后要改配置。详见《SpringCloud微服务》

253.(框架·易)Nacos 中 namespace 的主要作用是?

  • A. 隔离同一个项目里的不同模块
  • B. 用来做配置的灰度发布
  • C. 隔离 dev/test/prod 等环境,环境之间配置与服务互不可见
  • D. 决定配置项之间的加载优先级

答案:C 解析:namespace 隔离环境,group 隔离项目/模块,配置按 dataId + group + namespace 三元组定位——这条常被答反。详见《SpringCloud微服务》

254.(框架·中)认证全局过滤器实现了 getOrder() 返回 -1,另一个全局过滤器返回 1,执行顺序是?

  • A. 返回 -1 的先执行
  • B. 返回 1 的先执行
  • C. 两者并发执行,顺序不确定
  • D. getOrder 只对路由级过滤器生效,全局过滤器按注册顺序

答案:A 解析:Ordered 的数值越小优先级越高、越先执行,鉴权过滤器返回 -1 就是为了在业务过滤器之前拦下未登录请求。详见《SpringCloud微服务》

255.(框架·中)Nacos 临时实例默认每 5s 上报一次心跳,实例多久未心跳会被标记为不健康、多久被剔除?

  • A. 5s 不健康,15s 剔除
  • B. 15s 不健康,30s 剔除
  • C. 30s 不健康,60s 剔除
  • D. 一次心跳丢失即标记不健康并立即剔除

答案:B 解析:15s 未心跳标记不健康(此时仍会被调用方拿到,属于”带病实例”窗口),30s 未心跳才剔除;消费者本地还会缓存实例列表,注册中心短暂不可用时仍能按缓存调用。详见《SpringCloud微服务》

256.(框架·难)接口用 @SentinelResource(value = "getProduct", blockHandler = "getProductBlock", fallback = "getProductFallback") 标注,并给它配了 QPS=2 的快速失败流控规则;快速刷新触发限流时请求会?

  • A. 直接把 BlockException 抛给前端,返回 500
  • B. 走 getProductFallback 方法
  • C. 在队列中排队等待,直到令牌可用
  • D. 走 getProductBlock 方法返回兜底结果

答案:D 解析:blockHandler 专门处理限流/熔断等 BlockException(参数要与原方法一致、末尾多一个 BlockException),fallback 处理的是业务异常,两者别混。快速失败意味着不排队等待,直接走兜底。详见《SpringCloud微服务》

257.(单元测试·易)Mockito 的 @Mock 对象,没有被 when 打桩的方法被调用时返回什么?

  • A. 抛出 UnsupportedOperationException
  • B. 对象类型返回 null,基本类型返回 0/false
  • C. 返回真实实现的结果
  • D. 编译不通过,必须先给所有方法打桩

答案:B 解析:假对象默认返回空值,所以链式调用 when(mock.getA().getB()) 会直接 NPE——这是最常见的坑;另外 MockitoExtension 默认 strict stubs,多余的打桩会直接报错,逼你删掉无用的桩。详见《05-单元测试与工程质量》

258.(单元测试·中)关于 MockMvc,下列说法正确的是?

  • A. 不启动真实端口,把模拟请求直接交给 DispatcherServlet 处理,用于测 Web 层
  • B. 会启动内嵌 Tomcat 并监听随机端口,因此能测到过滤器完整链路
  • C. 必须连接真实数据库才能断言响应
  • D. 只能发送 GET 请求,POST 要用 TestRestTemplate

答案:A 解析:MockMvc 省去了真实网络与端口,配合 @WebMvcTest 只加载 Controller/Advice/序列化等 Web 层组件,Service 要用 @MockBean 顶替(切片里没有这个 Bean,用 @Mock 注入不进去)。需要覆盖过滤器/拦截器完整链路时,才改用 RANDOM_PORT 的真实端口方案。详见《05-单元测试与工程质量》

259.(单元测试·中)@ParameterizedTest @CsvSource({"' ', false", "'', false", "'写周报', true"}) 修饰的测试方法会怎样执行?

  • A. 只执行一次,取最后一行参数
  • B. 执行 3 次,每次注入 CSV 中的一行参数
  • C. 编译报错,参数化测试必须同时加 @Test
  • D. 随机挑选其中一行执行,用来做随机测试

答案:B 解析:参数化测试把 N 个用例写成 1 个方法,@CsvSource 每行一组参数;单参数可用 @ValueSource,复杂对象用 @MethodSource 提供 Stream。注意 @ParameterizedTest 本身已替代 @Test,再加 @Test 反而会因缺参数而失败。详见《05-单元测试与工程质量》

260.(单元测试·中)测试中 ArgumentCaptor 的作用是?

  • A. 捕获被 mock 方法实际收到的参数,用于断言传进去的到底是什么
  • B. 捕获被测方法抛出的异常并断言异常消息
  • C. 统计某个方法被调用的次数
  • D. 捕获被测类的日志输出并做断言

答案:A 解析:verify 只能断言”调没调、调几次”,参数捕获才能断言”传了什么”——例如校验落库对象里的 createBy 是否被正确填充;调用次数用 times()/never(),限时用 assertTimeoutPreemptively。详见《05-单元测试与工程质量》

261.(认证与安全·易)为什么”忘记密码”只能重置,不能把原密码发给用户?

  • A. 服务端存的是 BCrypt 这类单向哈希,本来就无法还原出原密码
  • B. 原密码只存在 Redis 里,过了期就取不到了
  • C. 原密码用 AES 加密存储,但密钥在重启后丢失
  • D. 只是合规要求,技术上完全可以还原

答案:A 解析:哈希是单向的,服务端连自己都无法还原——这正是”存的是哈希”的证明。要区分三个概念:哈希单向不可逆(存密码)、加密双向有密钥(存手机号等要还原的数据)、编码只是格式转换不算安全手段。详见《06-认证与安全》

262.(认证与安全·易)下面哪一项描述的是 XSS 及其正确防御?

  • A. 用户输入被拼进 SQL 改变语义,防御靠预编译占位符
  • B. 评论内容里的脚本被浏览器当成代码执行,防御靠输出转义、HttpOnly Cookie 与 CSP
  • C. 借用户已登录的 Cookie 诱导浏览器发出伪造请求,防御靠 SameSite 与 CSRF Token
  • D. 伪造 JWT 签名冒充他人,防御靠改用 RS256 并把密钥藏好

答案:B 解析:记忆锚点:SQL 注入是”数据在数据库被当代码执行”,XSS 是”数据在浏览器被当代码执行”,CSRF 是”请求冒充了合法用户”。HttpOnly 让脚本读不到 Cookie,正是为了压缩 XSS 窃取凭据的收益。详见《06-认证与安全》

263.(认证与安全·中)对同一个密码连续调用两次 encoder.encode(raw),得到的哈希串不同,原因是?

  • A. BCrypt 在结果里混入了时间戳,每次都不一样
  • B. BCrypt 会先把密码倒序,顺序随当前时间变化
  • C. 结果串被 Base64 编码时会引入随机字符
  • D. 每次都会随机生成盐并连同结果一起保存,比对时用同一个盐重算

答案:D 解析:盐是随机生成并写在哈希串里的,所以同一密码每次结果都不同,彩虹表直接失效;BCrypt 还是慢哈希,把 GPU 暴力破解的成本抬高几个数量级。比对必须用 matches(raw, encoded),不能自己再 encode 一次比字符串。详见《06-认证与安全》

264.(认证与安全·中)下列做法中,真正用于防御 CSRF 的是?

  • A. 对用户输入统一做 HTML 转义
  • B. 给会话 Cookie 加上 HttpOnly 属性
  • C. 校验 Origin/Referer,并使用 SameSite Cookie 与 CSRF Token
  • D. 全站强制 HTTPS 加密传输

答案:C 解析:CSRF 的核心是浏览器自动带上目标站 Cookie,所以要么让浏览器不自动带(SameSite),要么要求请求带一个攻击者拿不到的凭证(CSRF Token),要么校验来源。A、B 是 XSS 防御,D 防的是窃听与篡改,都挡不住伪造请求。加分点:Header + JWT 方案因为不会自动携带 Authorization 头,天然免疫大半。详见《06-认证与安全》

265.(认证与安全·难)JWT 无状态带来”注销难”,工程上较完整的解法是?

  • A. 把有效期设为永久,避免过期带来的登录中断
  • B. 服务端为每个签发的 JWT 保存完整会话,注销时删掉
  • C. 短期 Access Token + 可吊销的 Refresh Token,配合 Redis 黑名单与改密后强制失效
  • D. 注销时前端删除本地存储的 token 即可

答案:C 解析:JWT 的无状态是用”吊销难”换来的:服务端不存会话,只能等它自然过期。务实做法是缩短 Access Token 寿命、把长期凭证换成存库可吊销的 Refresh Token,再用 Redis 黑名单兜底;B 等于把 JWT 用回了 Session,D 毫无作用——token 仍然有效。详见《06-认证与安全》

266.(分布式与高并发·易)关于 MySQL 读写分离,下列说法正确的是?

  • A. 读写分离能同时解决读压力、写压力和数据量增长三个问题
  • B. 读写分离把读请求分摊到从库以缓解读压力,但不解决单表数据量与写压力
  • C. 读写分离要求从库与主库强一致,因此不存在复制延迟
  • D. 读写分离后所有查询都应走从库,包括下单后立刻查订单

答案:B 解析:主从复制是异步的,延迟通常毫秒级,但大事务、从库负载高时会放大到秒级,所以”写后立刻读”的关键路径必须强制走主库。读写分离只分担读;数据量增长与写压力要靠分库分表,这是两条不同的扩展路线。详见《02-高并发系统设计》

267.(分布式与高并发·难)某电商大促期间机房网络抖动,注册中心集群被切成两个分区。运维观察到:部分调用方拿到的实例列表里仍有已下线的节点,调用报错;但注册中心控制台仍能打开,也能继续注册新实例。该注册中心最可能是哪种取向,代价是什么?

  • A. CP 取向,因为控制台可用就证明数据一致
  • B. CP 取向,代价是重新选举期间整个集群拒绝服务
  • C. AP 取向,各分区独立对外服务,代价是分区期间可能读到过期的实例列表,需要调用方靠重试与熔断兜底
  • D. 与 CAP 无关,注册中心只负责存储,不参与任何取舍

答案:C 解析:节点对等、任一节点可用即可完成注册发现、宁可返回旧列表也不整体不可用,这是典型 AP 表现(Eureka、Nacos 默认的临时实例)。CP 的对照面是 ZooKeeper:主节点宕机后需重新选举,期间整个集群不可用(可达数十秒到两分钟),宁停服不脏读。选型看的是”业务能不能容忍读到旧实例列表”,而不是谁更先进。详见《01-分布式理论基础》

268.(分布式与高并发·易)缓存集群从 3 个节点扩到 4 个,运维只改了应用里的取模基数,随后数据库压力陡增。最直接的原因是?

  • A. 取模基数一变,绝大多数 key 的落点都变了,缓存几乎全部失效
  • B. 新节点上的槽位需要时间预热,预热期间读请求会穿透到数据库
  • C. 客户端没有预热,冷启动必然打满数据库
  • D. 4 个节点无法组成集群,必须使用奇数节点

答案:A 解析:hash(key) % N 的落点与 N 强绑定,N 从 3 变 4 时只有极少数 key 恰好落回原节点,等于整层缓存被清空。一致性哈希把取模换成”哈希环 + 顺时针寻址”,增删节点只影响相邻区段,迁移量约 1/N。要能顺口说出”为什么是 1/N”:只有新节点逆时针到上一个节点之间的那段数据需要搬家。详见《01-分布式理论基础》

269.(分布式与高并发·易)一致性哈希引入”虚拟节点”主要是为了解决什么问题?

  • A. 降低哈希计算的复杂度
  • B. 让节点之间自动同步数据副本
  • C. 保证任意时刻都能读到最新数据
  • D. 物理节点较少时在环上分布不均造成的数据倾斜

答案:D 解析:节点少的时候,几台机器在 2^32 的环上可能挤在一起,导致大部分数据压在同一台上。给每个物理节点生成 100~200 个虚拟节点散布到环上,数据先落到虚拟节点再映射回物理节点,分布就均匀了;某个节点下线时压力也会由多台分摊,而不是全给它的顺时针后继。详见《01-分布式理论基础》

270.(分布式与高并发·易)分库分表后引入全局 ID,除了唯一性,选型时最需要关注的性质是?

  • A. 生成的 ID 必须足够短
  • B. 趋势递增,让 B+ 树顺序插入、避免频繁页分裂
  • C. 必须包含业务含义,能从 ID 直接看出下单时间
  • D. 必须由数据库集中生成以保证绝对不重复

答案:B 解析:分表后单表自增只能保证表内唯一,必须引入全局发号器。趋势递增是关键:UUID 无序,插入时页分裂多、写放大严重,还占 36 个字符;雪花算法与号段模式都能趋势递增,且本地或半本地生成,不把发号变成新的单点。详见《01-分布式理论基础》

271.(分布式与高并发·中)某订单服务用雪花算法生成 ID,线上只部署了 1 个 worker。大促零点峰值下单量实测约 500 万 ID/秒,仅从雪花算法的结构看能否撑住?

  • A. 够,12 位序列号表示每毫秒 4096 个,合每秒 4096 万
  • B. 够,时间戳是毫秒级的,一毫秒内可以无限生成
  • C. 不够,单 worker 每毫秒最多 4096 个 ID,每秒上限约 409 万,需拆分 workerId 或改用号段模式
  • D. 不够,雪花算法的单机上限只有每秒 10 万,必须依赖 Redis 发号

答案:C 解析:12 位序列号在同一毫秒内只提供 0~4095 共 4096 个 ID,乘 1000 毫秒即约 409.6 万/秒,这是单 worker 的理论天花板;序列号在一毫秒内打满后只能阻塞到下一毫秒,表现为延迟毛刺。百万级 QPS 要么拆 workerId(10 位机器 ID 最多 1024 台),要么改用号段模式——一次取一段缓存在内存,把发号压力摊薄。详见《01-分布式理论基础》

272.(分布式与高并发·中)Redisson 的看门狗(watchdog)自动续期,在什么条件下才会生效?

  • A. 只要调用了 tryLock 就会生效
  • B. 只有方法上加了 @Transactional 才会生效
  • C. 锁被其他线程抢占后会自动续期
  • D. 加锁时没有显式指定 leaseTime 才生效,默认 30 秒过期、约每 10 秒续一次

答案:D 解析:不传 leaseTime 时 Redisson 默认给 30 秒过期,并启动后台线程按 1/3 周期(约 10 秒)检查续期,业务没执行完锁就不会失效。一旦显式传了 leaseTime,等于声明”超时我自己负责”,看门狗不再介入——业务跑超时也没人救,这是线上最常见的踩坑点。释放时配合 isHeldByCurrentThread() 判断,避免误删他人重新抢到的锁。详见《01-分布式理论基础》

273.(分布式与高并发·中)某服务用 Redis 单实例 + 主从(哨兵)实现分布式锁,以主节点写入成功为准。一次主节点宕机触发切换后,出现了两个请求同时进入扣减逻辑。原因是?

  • A. 主从复制是异步的,锁还没同步到从库主节点就宕机了,切换后新主节点上根本没有这把锁
  • B. 哨兵会自动删除所有带过期时间的 key
  • C. 从库提升为主库后会清空全部数据
  • D. SET NX 命令在主从架构下不生效

答案:A 解析:A 在旧主写锁成功,这条写命令尚未同步到从库,旧主就宕机了;从库被提升为新主后锁并不存在,B 于是也能加锁成功,互斥被打破。这是”Redis 锁可靠性中等”的根因,也是 Redlock 试图解决的问题。对绝对不能出错、且能接受性能代价的场景(选主、元数据变更),应换用 ZooKeeper 的临时节点锁。详见《01-分布式理论基础》

274.(分布式与高并发·中)用 ZooKeeper 实现公平锁时,每个客户端只监听自己前一个顺序节点的删除事件,而不是监听 /locks 下所有子节点的变化,目的是?

  • A. 减少 ZK 集群的磁盘写入量
  • B. 让锁支持可重入
  • C. 避免羊群效应:一次释放唤醒全部等待者,绝大多数抢不到还要重新注册监听,形成无效广播
  • D. 绕过 ZAB 协议过半提交的限制

答案:C 解析:如果所有人都监听父节点,每次释放锁都会唤醒全部等待者,它们抢不到锁只能重新注册,白白制造一轮惊群。只 watch 前一个节点,锁释放时恰好唤醒队首的下一个,天然形成公平队列。ZK 锁的另一优势是临时节点随会话消失,客户端宕机锁自动释放,不会死锁;代价是写要走 ZAB 过半提交,性能不如 Redis。详见《01-分布式理论基础》

275.(分布式与高并发·难)TCC 中的”悬挂”指的是哪种时序问题?

  • A. Try 先到、Confirm 迟迟不来,资源被长期冻结
  • B. Confirm 重试导致资源被重复扣减
  • C. 参与方没收到 Try 却先收到 Cancel,回滚了一笔不存在的业务
  • D. Cancel 先执行完,迟到的 Try 才落地并冻结资源,此后没有任何事务会再来解冻它

答案:D 解析:C 描述的是空回滚——Try 从未执行,Cancel 却先到,若不识别就会误改数据;D 才是悬挂,Cancel 已走完,迟到的 Try 又把资源冻上,这笔冻结永远不会被释放。两者都靠”事务状态表 + 幂等控制”化解:执行 Cancel 前先查/插入状态记录(区分”从未 Try”与”Try 成功”),Try 落地前也先校验状态,发现已回滚就拒绝执行。详见《01-分布式理论基础》

276.(分布式与高并发·难)关于”本地消息表”与”RocketMQ 事务消息”的可靠性差异,下列说法最准确的是?

  • A. 两者完全等价,只是把消息表放在了不同位置
  • B. 本地消息表把业务写入与消息记录放在同一个本地事务里,MQ 当时不可用也不影响业务提交;事务消息依赖 Broker 回查生产者,生产者长时间不可达时消息会悬而未决
  • C. 事务消息不依赖任何回查机制,因此可靠性一定强于本地消息表
  • D. 本地消息表必须依赖 XA 两阶段提交才能保证原子性

答案:B 解析:本地消息表的核心是”业务数据和待发消息在同一个本地事务里落库”,业务提交与消息留存天然原子,MQ 挂了也不影响下单,后台任务慢慢投递即可;代价是与业务耦合、消息表要定时清理。事务消息相当于把这张表搬进 Broker(半消息 + 定时回查),业务零侵入,但最终状态取决于生产者能否被回查触达。两者都是最终一致,不是强一致。详见《01-分布式理论基础》

277.(分布式与高并发·中)某接口用固定窗口限流,阈值 100 次/秒。客户端在某一秒的最后 0.1 秒发了 100 个请求,紧接着在下一秒的前 0.1 秒又发了 100 个,全部放行。这 0.2 秒里实际通过了多少请求,说明什么?

  • A. 100 个,限流器工作正常
  • B. 150 个,属于统计误差
  • C. 200 个,固定窗口在边界处最坏可放行 2 倍阈值,即临界突刺
  • D. 0 个,应当全部被拒绝

答案:C 解析:固定窗口只统计”当前这一秒”的计数,跨边界的两批请求分属不同窗口,各自都没超限,于是交界处最坏能通过 2 倍配额。滑动窗口把大窗口切成小格子统计”最近 N 秒”,把突刺摊平(Sentinel 采用);令牌桶则用桶容量限制瞬时突发。详见《02-高并发系统设计》

278.(分布式与高并发·难)某服务调用下游的超时率从 1% 涨到 60%,平均耗时从 20ms 涨到 80ms。熔断器跳闸进入”打开”状态后,正确的后续行为是?

  • A. 打开状态持续一个冷却窗口,其间请求直接快速失败;冷却结束进入半开,只放行少量探测请求,成功率恢复则关闭熔断器,否则重新打开并重新计时
  • B. 打开后永久拒绝,必须人工重启服务才能恢复
  • C. 打开后立刻放行全部请求,靠客户端重试自愈
  • D. 打开与关闭以固定 1 秒为周期交替切换

答案:A 解析:断路器是”关闭 → 打开 → 半开”的闭环。打开期间直接快速失败、不再真正调用下游,避免调用方线程被慢请求拖死形成雪崩;冷却结束进入半开,只放极少量探测请求试探下游是否恢复,成功则回到关闭,失败则回到打开重新计时。注意半开不等于预热放量(Warm Up):前者是试探,后者是让流量爬坡。另需留意,平均耗时会被长尾掩盖,熔断策略更常看慢调用比例与异常比例。详见《02-高并发系统设计》

279.(系统设计·中)要判断 10 亿条 URL 是否已经抓取过,团队先试了 Redis SET,算完内存账后放弃。若误判率要求约 1%,改用布隆过滤器大约需要多少内存?

  • A. 约 120MB
  • B. 约 12GB
  • C. 约 1.2GB
  • D. 约 120GB

答案:C 解析:Redis SET 存 10 亿条、单条约 32 字节,光数据就约 32GB,加上哈希表本身的桶开销更贵。布隆过滤器在 1% 误判率下约需 9.6 bit/元素,10 亿 × 9.6 bit ≈ 1.2GB,差了一个多数量级——这就是它赢的地方。代价是只能回答”可能存在 / 一定不存在”,且标准布隆不支持删除。详见《03-系统设计场景题》

280.(系统设计·难)承接上题,布隆过滤器的位数组长度 m 与哈希函数个数 k 需按容量 n 和目标误判率 p 推算。若 p 定为 1%,下列哪组取值最接近?

  • A. m/n ≈ 4 bit,k ≈ 2
  • B. m/n ≈ 9.6 bit,k ≈ 7
  • C. m/n ≈ 20 bit,k ≈ 14
  • D. m/n ≈ 1 bit,k ≈ 1

答案:B 解析:由 m = -n·lnp/(ln2)² 与 k = (m/n)·ln2 可得:p=1% 时约 9.6 bit/元素、k≈7;p=0.1% 时约 14.4 bit/元素。k 不是越大越好,超过最优值后位数组被置 1 的比例上升,误判率反而回升。工程上容量 n 估小了会让实际 p 迅速恶化,所以通常按预估峰值的两倍以上设计并定期重建。详见《03-系统设计场景题》

281.(系统设计·中)短链服务预计每年新增 1 亿条,团队坚持用自增 ID + Base62 生成短码,长度固定 6 位。仅从编码容量看,6 位够用多久?

  • A. 不够,62^6 只有约 5.7 亿,两年就耗尽
  • B. 够用 3 年,之后必须换成 8 位
  • C. 不够,Base62 只能表示纯数字,承载不了 1 亿条
  • D. 非常宽裕,62^6 ≈ 568 亿,按每年 1 亿条也能用几百年

答案:D 解析:Base62 用 0-9、A-Z、a-z 共 62 个字符作”数字”,6 位空间为 62^6 ≈ 568 亿,按 1 亿/年算足够用几百年。所以短链的瓶颈从来不在短码长度而在读:读多写少约 100:1,热点短码靠本地缓存 + Redis 两级缓存扛,不存在的短码用布隆过滤器提前挡掉,避免每一次瞎猜都打穿到 DB。详见《03-系统设计场景题》

282.(系统设计·难)秒杀扣减库存有三种常见做法:① DB 直接 UPDATE stock SET num=num-1 WHERE id=? AND num>0;② Redis + Lua 预扣成功后发 MQ 异步落库;③ 请求全量进 MQ,消费端串行扣减。某活动 100 万用户抢 1 万件,峰值读 QPS 约 10 万、真正下单写约 1 万。下列判断正确的是?

  • A. 只用 ① 会让 10 万级读请求全部打到 DB、连接池瞬间打满;② 把绝大部分无效流量挡在 Redis 层,DB 只承接队列匀速来的写,因此是主流方案;③ 的代价是用户长时间排队等待、反馈延迟大
  • B. ① 最可靠,应当用它扛下全部流量
  • C. ② 只要 Redis 预扣成功就绝不会超卖,DB 层不需要再兜底
  • D. ③ 最优,因为它能让每个请求都立刻拿到结果

答案:A 解析:这题考”每一层挡掉什么”。DB 条件更新(AND num > 0)是防超卖的最后一道保险,行锁加上影响行数为 0 即回滚,但它扛不住 10 万级 QPS;Redis + Lua 把”查库存 + 扣减”做成原子操作,在内存里挡掉绝大多数无效请求,再发 MQ 让 DB 匀速落库;全量进 MQ 等于把压力推给排队,用户拿不到即时反馈。C 的陷阱在于 Redis 与 DB 之间仍有不一致窗口(宕机丢数据、超时未支付要回补),所以那层条件更新必须保留,并配合定时对账。详见《02-高并发系统设计》

283.(系统设计·易)订单 15 分钟未支付要自动关闭。下列方案中,最不推荐用于生产环境的是?

  • A. RocketMQ 延迟消息 / 定时消息
  • B. RabbitMQ 死信队列 + TTL(或延迟插件)
  • C. JDK 的 DelayQueue,服务重启后重新装载一遍
  • D. Redis ZSET,score 存执行时间,后台轮询 ZRANGEBYSCORE

答案:C 解析:DelayQueue 是纯内存的优先级队列,单机、重启即全丢,多实例部署时每个实例还会各关一遍单。它适合进程内的短延时任务,不适合承担订单超时这类必须可靠触发的业务。生产上优先 MQ 延迟消息(业务解耦、消息不丢),量大时用时间轮或调度平台;无论哪种实现,关单动作都要幂等并带状态校验。详见《03-系统设计场景题》

284.(系统设计·中)关单任务与支付成功回调几乎同时到达,最稳妥的防冲突做法是?

  • A. 给关单加分布式锁,支付回调不加锁
  • B. 两边都用条件更新,例如关单执行 UPDATE orders SET status=已关闭 WHERE id=? AND status=待支付,影响行数为 0 说明已被回调改过,直接放弃
  • C. 把关单时间设在支付超时之后 1 秒,靠时间差避开
  • D. 先 SELECT 查一次状态,是待支付就关单

答案:B 解析:A 只给一边加锁,另一边照样能改,等于没锁;D 的”先查后改”之间存在窗口——查完是待支付,改之前回调已把状态改成已支付,结果出现”已支付却被关闭”。正确做法是把判断与修改合并成一条带状态条件的 UPDATE,借数据库行锁保证原子性,两边都这么做,谁先到谁生效,这就是状态机 + 条件更新的通用写法。详见《03-系统设计场景题》

285.(系统设计·中)微博类 Feed 流中,某大 V 有 3000 万粉丝。若采用写扩散(推模式),发一条微博会发生什么?通常怎么处理?

  • A. 没有问题,推模式读性能最好,应对所有用户统一使用
  • B. 只影响大 V 自己的发博耗时,对系统整体无影响
  • C. 应改为所有人统一用读扩散,彻底放弃写扩散
  • D. 一次发文要写 3000 万份收件箱,写放大严重且会长时间占满队列;标准做法是推拉结合,按粉丝量设阈值(如 10 万),大 V 走读扩散、普通用户走写扩散

答案:D 解析:推模式把成本放在写侧,粉丝千万级时一次发文就是千万次写入,即便分批 + 异步也会长时间积压;拉模式把成本放在读侧,粉丝一刷新就要聚合关注列表,延迟高。推拉结合按粉丝量划线:普通用户写扩散保证读得快,大 V 走读扩散避免写爆炸,粉丝读时再把大 V 的内容合并进来。收件箱一般只存最近 N 条 ID,翻页回源 DB,删除与可见性在读取时过滤。详见《03-系统设计场景题》

286.(工具链·易)已经执行过 git add,但还没提交,现在想让文件退出暂存区、已做的修改保留在工作区,应该用?

  • A. git restore --staged <file>
  • B. git restore <file>
  • C. git reset --hard HEAD
  • D. git rm --cached <file> 后再重新 add

答案:A 解析:restore 不带参数撤的是工作区(改动作废),加了 —staged 撤的是暂存区(改动保留);老写法 git reset HEAD <file> 同义。git rm --cached 是把文件移出版本控制,文件会变成未跟踪状态,不是”退出暂存”的正确姿势。详见《04-开发工具链-Git-Maven-Linux》

287.(工具链·易)Maven 坐标中,用于唯一标识一个构件所必需的三要素是?

  • A. groupId、packaging、classifier
  • B. artifactId、scope、version
  • C. groupId、artifactId、version
  • D. groupId、artifactId、packaging

答案:C 解析:packaging 缺省为 jar、classifier 缺省为空,用于区分同一 POM 产出的不同构件,都不是定位构件的必需项;scope 只决定依赖在哪几套 classpath 上生效,与”找到哪个包”无关。详见《04-开发工具链-Git-Maven-Linux》

288.(工具链·易)线上停服务时先执行 kill -15 <pid> 而不是直接 kill -9 <pid>,主要原因是?

  • A. -15 更快,-9 要等内核调度一轮才生效
  • B. -15 发送 SIGTERM,进程可以捕获并执行 shutdown hook 做优雅下线;-9 是 SIGKILL,进程没有清理机会
  • C. -9 需要 root 权限,普通用户发不出去
  • D. -15 只是把进程挂起,-9 才真正结束进程

答案:B 解析:SIGTERM 可被捕获,JVM 会跑 shutdown hook、Spring 能优雅摘流量再下线;SIGKILL 由内核直接终止,连接、锁、临时文件、消息 ACK 都可能残留,所以生产先试 -15。详见《04-开发工具链-Git-Maven-Linux》

289.(工具链·中)对已经 push 到公共 develop 分支的三个提交执行 git rebase -i 压缩后强推,会带来什么后果?

  • A. 只有自己的本地仓库受影响,其他人毫无感知
  • B. 远程仓库会拒绝一切强推,这个命令必然失败
  • C. 提交被重写成新的 sha,协作者本地仍指向旧历史,各自拉取或强推会互相冲掉对方的提交
  • D. 三个提交的内容会永久丢失,reflog 也捞不回来

答案:C 解析:“已经 push 到公共分支的提交禁止 rebase”针对的就是 sha 被重写这一点:内容没变,但所有下游都成了孤儿历史。D 错在”永久丢失”——内容还在悬空对象里,可借助 reflog 找回。详见《04-开发工具链-Git-Maven-Linux》

290.(工具链·中)要一条命令统计 access.log 中 5xx 响应占全部请求的比例,awk 写法正确的是?

  • A. awk '$9>=500{e++} END{print e/NR}' access.log
  • B. awk '$9==500' access.log | wc -l
  • C. grep 500 access.log | sort -rn | head
  • D. awk '{print $9}' access.log | uniq -c

答案:A 解析:awk 逐行累加 5xx 计数,NR 是总行数,END 块里相除即得占比;B 只统计了恰好 500 的行、也没有算比值,C 排序后取头部与比例无关,D 少了 uniq 需要的预先排序。详见《04-开发工具链-Git-Maven-Linux》

291.(工具链·难)误执行 git reset --hard HEAD~2 之后,git reflog 能帮你做到什么?

  • A. 只能恢复工作区里还没提交过的改动,已提交内容一律找不回
  • B. 能把 reset 丢掉的提交找回来——只要对应 sha 还出现在 reflog 记录里、对象尚未被 gc 回收
  • C. 能把分支恢复成任意历史状态,包括从未被 HEAD 指向过的提交
  • D. 只是把 reset 命令重新执行一遍,本身不含恢复能力

答案:B 解析:reflog 记录的是 HEAD 的每一次移动,git reset --hard <reflog 中的 sha> 就能把分支挪回去;局限在于它只覆盖”曾经成为过 HEAD”的位置,且悬空对象过了 gc 期限就真的没了。详见《04-开发工具链-Git-Maven-Linux》

292.(工具链·中)mvn install 与 mvn deploy 的关键区别是?

  • A. install 只打包不跑测试,deploy 会重新执行测试
  • B. install 把构件装进本地 ~/.m2 仓库供本机其他项目依赖,deploy 推送到远程私服供团队共享
  • C. 两者完全等价,deploy 只是 install 的别名
  • D. install 会跳过 compile 阶段,deploy 不会

答案:B 解析:生命周期是一条顺序链,两个命令都是”执行到该阶段并连带跑完前面所有阶段”,所以都会经过 compile 与 test;差别只在终点:一个是本地仓库,一个是远程私服 Nexus。详见《04-开发工具链-Git-Maven-Linux》

293.(工具链·难)依赖树如下:A→B→C→X:1.5(深度 3)、A→D→X:1.0(深度 2),且 A 的 pom 中 B 声明在 D 之前。最终生效的 X 版本是?

  • A. 1.5,因为同深度时先声明者胜,B 在 D 前面
  • B. 两个版本都会被打进包里,由类加载顺序决定用哪个
  • C. 直接报依赖冲突错误,构建失败
  • D. 1.0,因为路径最短者优先,声明顺序只在路径等长时才参与裁决

答案:D 解析:Maven 依赖调解先比路径深度、再比声明顺序,两个原则冲突时”路径短的完胜”,所以这里声明顺序根本轮不上。1.0 生效后若 C 用到了 1.5 才有的方法,运行期就会抛 NoSuchMethodError——这也解释了为什么这类错误编译期不报。详见《04-开发工具链-Git-Maven-Linux》

294.(设计模式·易)枚举单例被《Effective Java》推荐为最佳写法,主要因为它天然免疫了哪两种破坏手段?

  • A. 反射强开私有构造、反序列化时新建实例
  • B. 多线程并发创建、指令重排序
  • C. 类加载器重复加载、JIT 激进优化
  • D. 内存溢出、GC 提前回收对象

答案:A 解析:JVM 保证枚举常量的构造只执行一次,反射创建枚举会直接被 JDK 拒绝;枚举的序列化按名字查找、反序列化返回同一常量。多线程安全只是它顺带的好处,不是”天然免疫破坏”所指的靶点。详见《07-设计模式》

295.(设计模式·易)用策略模式重构一长串 if ("WECHAT".equals(type)) ... else if ...,Spring 项目里最常见的落地方式是?

  • A. 把所有分支方法改成 static,避免反复创建对象
  • B. 用更长的 switch 替换 if-else,提升可读性
  • C. 把分支逻辑整体上移到父类,让子类重写整个方法
  • D. 每个分支抽成一个策略实现类,用 Map<String, 策略> 分发(构造器注入 List<策略> 自动收集)

答案:D 解析:注入 List<PayStrategy> 后按 type() 建表,新增渠道只需加一个 @Component 类、Context 一行不改,这正是开闭原则的落地。C 描述的是模板方法的形状,而且”重写整个方法”并没有消除分支。详见《07-设计模式》

296.(设计模式·中)装饰器模式与代理模式结构相似,核心差别在于?

  • A. 装饰器只能包装一个对象,代理可以同时包装多个
  • B. 装饰器关注给对象叠加功能、通常由调用方层层包装;代理关注控制对目标的访问、通常由框架生成
  • C. 装饰器基于继承实现,代理基于组合实现
  • D. 装饰器必须继承目标类,代理必须实现与目标相同的接口

答案:B 解析:new BufferedReader(new InputStreamReader(new FileInputStream(f))) 是调用方手工套娃,一层加一种能力(字节→字符→缓冲);AOP 生成的代理则是框架替你控制访问。结构可以一样,意图不同才是区分点。详见《07-设计模式》

297.(设计模式·中)Servlet 的 Filter 链与策略模式最关键的区别是?

  • A. Filter 链上只有一个过滤器会真正生效
  • B. 策略模式的多个实现类在运行期也会被依次全部执行
  • C. Filter 链上多个处理者会依次处理同一个请求、顺序本身有语义;策略模式通常只命中一个实现
  • D. 两者没有本质区别,Filter 链就是策略模式的一种写法

答案:C 解析:chain.doFilter() 把请求交给下一个处理者,可以全链走完也可以中途断链,注册顺序会改变行为;策略是客户端选定一个算法后命中即止。这也是”责任链可能多个处理者参与、策略只命中一个”的由来。详见《07-设计模式》

298.(设计模式·中)AQS 的 acquire() 固定了”尝试获取→失败入队→阻塞等待”的骨架,把 tryAcquire 留给子类实现,这属于哪种模式?

  • A. 策略模式:整个算法可以在运行期被替换
  • B. 责任链模式:请求沿处理者链传递
  • C. 模板方法模式:父类定算法骨架,把某些步骤作为钩子留给子类
  • D. 观察者模式:状态变化后自动通知订阅者

答案:C 解析:ReentrantLock、Semaphore、CountDownLatch 各自实现 tryAcquire/tryRelease,但排队与唤醒的流程骨架完全复用、编译期就固定。策略与模板方法的分界正在这里:前者整个算法可换,后者只有局部步骤可变。详见《07-设计模式》

299.(设计模式·难)关于单例被”反射”与”序列化”两种手段破坏及对应防御,正确的是?

  • A. 反射可以通过 setAccessible(true) 强开私有构造并新建实例,在构造器里判空抛异常可挡住;反序列化走的是另一条路,需要重写 readResolve() 返回原实例
  • B. 反射无法突破私有构造,能破坏单例的只有序列化
  • C. 只要实现了 readResolve(),反射也就无法再创建新实例了
  • D. 把类声明为 final 之后,反射与序列化两种手段都会失效

答案:A 解析:getDeclaredConstructor().setAccessible(true).newInstance() 确实能绕过私有构造;而反序列化根本不走构造器,是 readObject 直接造对象,所以必须用 readResolve 单独兜住——两种手段要分别防,没有一招通吃。详见《07-设计模式》

300.(设计模式·难)策略模式与状态模式最本质的区别是?

  • A. 策略模式靠继承实现,状态模式靠组合实现
  • B. 策略模式各算法之间互不相识、由客户端选定;状态模式各状态之间有迁移规则、由状态自身决定下一个状态
  • C. 状态模式消除不了 if-else,只有策略模式能消除
  • D. 策略模式只能有一个实现类,状态模式可以有多个

答案:B 解析:两者都能把分支拆成对象、都能配合 Map 分发,差别在语义:状态模式带状态机(订单状态流转、线程 NEW→RUNNABLE→TERMINATED),迁移由状态自己驱动,调用方通常不关心当前处于哪个状态。详见《07-设计模式》

301.(设计模式·难)目标类没有实现任何接口,并且被声明为 final,Spring 仍想对它做 AOP 增强,结果是?

  • A. 自动改用 JDK 动态代理,一切照常
  • B. CGLIB 会退化成静态代理,依然可以增强
  • C. 只要方法不是 private,final 类也能被 CGLIB 代理
  • D. CGLIB 生成子类会失败,无法为该类创建代理,只能去掉 final 或改为面向接口设计

答案:D 解析:没有接口时 JDK 动态代理无从下手,只能靠 CGLIB;而 CGLIB 的原理是生成目标类的子类,“不能继承”直接判了死刑,同理 final/private/static 方法也覆盖不了。记住这条边界,就知道为什么框架常要求业务类面向接口设计。详见《07-设计模式》

302.(算法·易)下列排序算法中,最坏情况下仍能保证 O(nlogn) 的是?

  • A. 快速排序
  • B. 归并排序
  • C. 冒泡排序
  • D. 插入排序

答案:B 解析:快排每次划分极不均匀时会退化到 O(n²),例如对已经有序的数组固定取首元素当基准;归并排序每层都对半切分,稳定 O(nlogn),代价是需要 O(n) 的额外空间。详见《03-算法刷题路线》

303.(算法·中)代码 for (int i = 1; i < n; i *= 2) { for (int j = 0; j < n; j++) sum++; } 的时间复杂度是?

  • A. O(n)
  • B. O(logn)
  • C. O(nlogn)
  • D. O(n²)

答案:C 解析:外层 i 每次翻倍,只执行 logn 次;内层固定 n 次,相乘得 O(nlogn)。判断这类题要看每层循环的”步进方式”而不是嵌套层数——两层循环不一定是 O(n²)。详见《03-算法刷题路线》

304.(算法·难)对 n 个元素自底向上建堆(heapify)的时间复杂度是?

  • A. O(n):越靠近底层的节点数量越多、但下沉高度越小,加权求和后收敛到常数倍 n
  • B. O(nlogn):要处理 n 个节点,每次下沉最坏 O(logn)
  • C. O(logn):只有根节点需要下沉
  • D. O(n²):每个节点都要和其余所有节点比较

答案:A 解析:这是经典的”上界不等于紧界”:n 次 O(logn) 只是上界,实际求和是 Σ(每层节点数 × 该层高度),收敛到 O(n)。注意逐个 insert 建堆才是 O(nlogn),这也是堆排序整体复杂度仍为 O(nlogn) 的原因。详见《03-算法刷题路线》

305.(算法·中)用 LinkedHashMap 实现 LRU 缓存,构造器参数与需要重写的方法分别是?

  • A. super(capacity, 0.75f, false) + 重写 removeEldestEntry
  • B. super(capacity) + 重写 put
  • C. super(capacity, 0.75f, true) + 重写 removeEldestEntry
  • D. super(capacity, 0.75f, true) + 重写 get

答案:C 解析:第三个参数 accessOrder=true 才会按访问顺序维护链表,get 也算一次访问;传 false 就成了 FIFO,淘汰的是最早插入而非最久未用的元素。removeEldestEntry 里返回 size() > capacity 即触发淘汰链表头。详见《03-算法刷题路线》

306.(算法·难)从 n 个数中找第 K 大的元素,“维护一个大小为 K 的小顶堆”这种做法的时间复杂度是?

  • A. O(nlogn):每个元素都要进堆排一次序
  • B. O(nlogK):遍历 n 个元素,每次堆调整 O(logK)
  • C. O(Klogn):堆里始终只有 K 个元素
  • D. O(n):堆的插入删除都是常数时间

答案:B 解析:遍历是 O(n),每次入堆/出堆是 O(logK),合计 O(nlogK);K 远小于 n 时明显优于整体排序的 O(nlogn),这正是”海量数据取 Top K”的标准解。选小顶堆是为了让堆顶正好是当前第 K 大,超容量就弹掉。详见《03-算法刷题路线》

307.(算法·中)下面哪种二分写法在收缩区间时可能陷入死循环?

  • A. 左闭右闭:while (left <= right),配 left = mid + 1 / right = mid - 1
  • B. 左闭右开:while (left < right),配 left = mid + 1 / right = mid
  • C. while (left <= right) 且 mid = left + (right - left) / 2
  • D. while (left < right) 且 mid = (left + right) / 2,并用 left = mid 收缩左边界

答案:D 解析:下取整的 mid 在区间只剩两个元素时会等于 left,若还用 left = mid 收缩,left 永远不动就死循环。两个修法:改成 left = mid + 1,或把 mid 改成上取整 (left + right + 1) / 2——一句话,收缩方向必须和取整方向配套。详见《03-算法刷题路线》

填空题

308.(Java基础·易)Java 源文件经 javac 编译后生成___文件,由 JVM 加载执行。

答案:字节码/class/.class 解析:javac 把 .java 编译为 .class 字节码,JVM 解释/JIT 执行,这是”一次编写,到处运行”的基础。详见《01-环境搭建与基础语法》

309.(Java基础·易)== 用于比较引用类型时,比较的是___。

答案:地址/内存地址/引用地址 解析:比较内容应使用重写后的 equals,String、Integer 等都已重写。详见《02-面向对象与核心类》

310.(集合·易)ArrayList 的底层是___,LinkedList 的底层是___。

答案:数组;双向链表 解析:ArrayList 随机访问快,LinkedList 头尾插删快,随机访问慢。详见《03-集合与泛型》

311.(集合·易)HashMap(JDK 1.8)的底层结构是___+链表+红黑树。

答案:数组/哈希表 解析:数组是主体,冲突挂链表,链表长度≥8 且数组长度≥64 时树化。详见《03-集合与泛型》

312.(并发·中)Executors 创建的 FixedThreadPool 使用无界___,任务堆积可能导致 OOM,因此推荐手动 new ThreadPoolExecutor。

答案:队列/阻塞队列 解析:无界队列在消费慢时会无限堆积任务,最终内存耗尽。详见《01-多线程与并发编程》

313.(并发·中)synchronized 是 JVM 层面的锁,而 ReentrantLock 是___层面用 AQS 实现的锁。

答案:JDK/API/Java 解析:ReentrantLock 基于队列同步器(AQS)在 Java 代码层实现,功能更丰富:可中断、超时、公平锁、Condition。详见《01-多线程与并发编程》

314.(JVM·中)JVM 新生代分为 Eden 区和两个___区。

答案:Survivor/S0/S1/from/to 解析:默认 8:1:1,对象在两个 Survivor 区之间来回复制,每熬过一次 Minor GC 年龄+1。详见《02-JVM核心》

315.(JVM·中)类加载过程依次为:加载 → 验证 → 准备 → ___ → 初始化。

答案:解析 解析:准备阶段给静态变量赋零值,初始化才执行 ;解析将常量池符号引用转为直接引用。详见《02-JVM核心》

316.(MySQL·易)MySQL 5.5 之后的默认存储引擎是___。

答案:InnoDB 解析:支持事务、行级锁、外键与 MVCC;MyISAM 表锁且无事务,已非默认。详见《MySQL基础与SQL实战》

317.(MySQL·易)MySQL 默认的事务隔离级别是___。

答案:可重复读/REPEATABLE READ/RR 解析:通过 MVCC + 临键锁在该级别下大幅避免幻读。详见《索引-事务-锁与调优》

318.(MySQL·中)EXPLAIN 输出的 type 列出现___时表示全表扫描,通常需要优化。

答案:ALL 解析:性能从好到差:const > eq_ref > ref > range > index > ALL,生产 SQL 至少做到 range。详见《索引-事务-锁与调优》

319.(Redis·易)Redis 中实现排行榜最常用的数据结构是___。

答案:ZSet/zset/有序集合/sorted set 解析:按 score 排序,ZINCRBY 加分、ZREVRANGE 取 TopN。详见《01-Redis》

320.(Redis·易)缓存三大经典问题:缓存穿透、缓存击穿、缓存___。

答案:雪崩 解析:穿透查不存在的数据,击穿是热点 key 过期瞬间被打穿,雪崩是大规模 key 同时失效。详见《01-Redis》

321.(Redis·中)Redis 分布式锁的加锁命令 SET key value NX ___ ttl 中,最后一参数段表示过期时间,防止死锁。

答案:EX/ex 解析:NX 保证互斥,EX 设置过期;生产建议直接用 Redisson 看门狗自动续期。详见《01-Redis》

322.(消息队列·中)Kafka 的消息顺序只在___范围内保证,跨分区无序。

答案:分区/Partition/partition 解析:按 key 路由到同一分区可保证同类消息有序,消费组内一个分区只由一个消费者消费。详见《03-Kafka》

323.(框架·中)SpringBoot 中把配置文件批量绑定到配置类的注解是 @___。

答案:ConfigurationProperties 解析:@Value 适合单个值;@ConfigurationProperties(prefix=“xxx”) 支持批量绑定与校验。详见《SpringBoot实战》

324.(框架·易)SpringMVC 的前端核心控制器是___。

答案:DispatcherServlet 解析:所有请求统一经过它分发到 HandlerMapping → HandlerAdapter → Controller。详见《Spring核心》

325.(分布式与高并发·中)Nacos 同时支持 AP 与___两种模式,可按场景切换。

答案:CP 解析:临时实例走 AP(Distro 协议),持久实例走 CP(Raft);ZK 是 CP,Eureka 是 AP。详见《06-ZooKeeper与Nacos》

326.(分布式与高并发·中)雪花算法依赖机器时钟,时钟回拨可能导致生成___的 ID。

答案:重复ID/ID重复/重复 解析:兜底方案:等待追平、拒绝发号、使用备用机器 ID 或改用号段/Leaf 等方案。详见《01-分布式理论基础》

327.(分布式与高并发·中)秒杀系统中常用 Redis+___脚本原子地”判断库存并扣减”,防止超卖。

答案:Lua/lua 解析:多个 Redis 命令在 Lua 中原子执行,配合 MQ 异步落库完成秒杀主链路。详见《02-高并发系统设计》

328.(Java8·中)Stream 的中间操作是___执行的,遇到终端操作才真正计算。

答案:惰性/延迟 解析:filter/map 等只记录操作,collect/forEach 触发才执行;这也是流只能被消费一次的原因。详见《05-Lambda-Stream与Java8新特性》

329.(网络·易)TCP 断开连接需要___次挥手。

答案:4/四 解析:TCP 全双工,两个方向各需一次 FIN 和一次 ACK。详见《03-IO模型与网络编程》

330.(Nginx·中)Nginx 把动态请求转交给后端应用的核心指令是___。

答案:proxy_pass 解析:写在 location 块中;静态资源用 root/alias,动静分离即由此组合实现。详见《07-Nginx》

331.(框架·中)Spring Cloud Gateway 中,定义”哪些请求转发到哪个服务”的配置叫___规则。

答案:路由/Route 解析:路由 = 断言(Predicate,匹配条件)+ 过滤器(Filter,处理逻辑),是网关的核心抽象。详见《SpringCloud微服务》

332.(Elasticsearch·中)MySQL 与 ES 近实时同步,常用阿里开源的___监听 binlog 异步写入 ES。

答案:canal 解析:canal 伪装成 MySQL 从库解析 binlog;其他方案还有同步双写与 MQ 异步,各有取舍。详见《05-Elasticsearch》

333.(MongoDB·易)MongoDB 中对应关系型数据库”行”的概念叫___。

答案:文档/document 解析:集合 collection ≈ 表;文档以 BSON(二进制 JSON)存储。详见《08-MongoDB》

334.(Redis·难)Redis 主从增量复制依赖主节点的___环形缓冲区,缓冲区太小或 offset 不连续就会退化成___复制。

答案:repl_backlog;全量 解析:从节点上报 master_repl_offset,只要在 backlog 范围内就只补差额;否则重新 fork 做全量同步,大内存实例 fork 会带来毫秒级阻塞。详见《01-Redis》

335.(Redis·难)分布式锁”误删别人的锁”发生在:客户端 A 持锁后长时间 GC 停顿,锁___后被 B 获取,A 恢复后直接 DEL 删掉了 B 的锁;解法是 value 存唯一标识并加___原子校验删除。

答案:过期;Lua/lua/lua脚本 解析:判断与删除必须是两步,普通代码非原子;Redisson 用看门狗续期避免过期,用 Lua 校验持有者避免误删。详见《01-Redis》

336.(MySQL·难)RR 下事务第一次快照读时生成 ReadView,此后___(填”复用/每次重新生成”);RC 则是每次___都重新生成。

答案:复用;查询/select/快照读 解析:ReadView 记录了生成时仍活跃的事务 ID 集合,据此判断版本可见性;复用同一份快照正是”可重复读”的实现手段。详见《索引-事务-锁与调优》

337.(操作系统·中)Linux 下递归搜索目录内所有文件的文本内容,常用 grep 的 ___ 参数(写作两个字母或长选项)。

答案:-r/-R/—recursive 解析:grep -rn "关键词" 目录 会递归并显示行号;-R 与 -r 的差别是对符号链接的处理,管道配合 | head 控制输出量。详见《04-开发工具链-Git-Maven-Linux》

338.(工具链·中)要确认本机某端口是否被进程监听,常用命令是 ___(含参数)。

答案:ss -lntp/ss -lnt/netstat -anp/lsof -i:端口 解析:-l 只看 LISTEN、-n 不做 DNS 反解、-t 只看 TCP、-p 显示进程;ss 比 netstat 更快,容器里没 ss 时可用 lsof。详见《04-开发工具链-Git-Maven-Linux》

339.(Redis·难)ZSET 在元素较少、分值较短时用___编码存储;超过阈值后改为跳表+字典表双结构,其中跳表的作用是支持按分数区间 O(logN) 查找。

答案:ziplist/压缩列表/listpack 解析:小 ZSET 用 ziplist/listpack 连续内存省开销;变大后拆成 skiplist(按 score 范围与排名查找)+ dict(按成员 O(1) 取分数)。详见《01-Redis》

340.(Java基础·易)Java 中所有类的直接或间接父类是___。

答案:Object/java.lang.Object 解析:Object 是类层次根,定义了 equals/hashCode/toString/wait/notify 等所有对象共有的方法。详见《02-面向对象与核心类》

341.(集合·易)HashMap 解决哈希冲突在 JDK 1.8 中采用___法,冲突严重时转为红黑树。

答案:链地址法/拉链法 解析:同桶元素挂成链表,长度≥8 且表长≥64 时树化;1.7 是纯链表加头插,1.8 改为尾插。详见《03-集合与泛型》

342.(集合·中)ArrayList 扩容时新容量为旧容量的___倍。

答案:1.5/1.5倍 解析:newCapacity = oldCapacity + (oldCapacity >> 1),与 HashMap 的 2 倍扩容是两套不同策略。详见《03-集合与泛型》

343.(Java8·中)Stream 的中间操作是___求值的,只有遇到终端操作才会真正执行。

答案:惰性/lazy 解析:filter/map 等只记录流水线,collect/forEach 等终端操作才拉动执行;这也是 peek 不接终端操作不生效的原因。详见《05-Lambda-Stream与Java8新特性》

344.(Java基础·中)Java 中 String 与 StringBuilder 的核心区别是:String 是___的,StringBuilder 是可变的。

答案:不可变/immutable 解析:String 每次拼接产生新对象,循环拼接应用 StringBuilder 避免大量临时对象。详见《02-面向对象与核心类》

345.(Java基础·易)Java 支持___继承,一个类可以实现多个接口。

答案:单/单一 解析:单继承多实现是 Java 的设计取舍,避免 C++ 菱形继承的歧义问题;接口多实现配合 Java 8 default 方法需要显式消歧。详见《02-面向对象与核心类》

346.(集合·中)保证元素唯一性的 HashSet,底层实际是维护了一个___。

答案:HashMap/哈希表 解析:HashSet 把元素当 key 存入 HashMap,value 是统一的占位对象,因此去重依赖 hashCode+equals。详见《03-集合与泛型》

347.(Java基础·中)finally 块中若执行 return,会___ try 块中已有的返回值。

答案:覆盖/覆盖掉 解析:finally 在方法返回前执行,其中的 return 会替换原返回值;同理 try 里有 return、finally 里改基本类型变量不会影响已确定的返回值。详见《04-异常-IO-反射-注解》

348.(集合·中)List、Set、Map 三大集合中,___不属于 Collection 接口体系。

答案:Map 解析:Collection 分支是 List/Set/Queue,Map 是独立顶层接口,以键值对存储,两者无继承关系。详见《03-集合与泛型》

349.(Java8·中)方法引用 String::length 等价于 Lambda 表达式___。

答案:s -> s.length()/str -> str.length() 解析:方法引用是 Lambda 的语法糖,静态方法用 类::方法名,实例方法用 对象::方法名,构造器用 类::new。详见《05-Lambda-Stream与Java8新特性》

350.(集合·中)把数组转成固定长度的 List 常用 Arrays.___ 方法,但它不支持 add/remove。

答案:asList 解析:asList 返回的是 Arrays 内部类视图,长度固定;要可变列表需 new ArrayList<>(Arrays.asList(arr))。详见《03-集合与泛型》

351.(Java基础·中)String.intern() 的作用是把字符串放入___,返回其规范表示。

答案:字符串常量池/常量池/StringTable 解析:JDK 7 起常量池移到堆中,intern 用于复用重复字符串、节省内存;大量重复字符串场景是优化点。详见《02-面向对象与核心类》

352.(集合·中)用 Stream 把 List 转 Map 时,若 key 重复会抛 IllegalStateException,需传入___参数指定合并策略。

答案:合并函数/mergeFunction/BinaryOperator 解析:toMap(keyMapper, valueMapper, (a,b) -> b) 三参重载用合并函数消解冲突,否则默认 throwDuplicates。详见《05-Lambda-Stream与Java8新特性》

353.(Java基础·中)Integer a = 100; Integer b = 100; a == b 的结果是___。

答案:true 解析:-128~127 内命中 IntegerCache 返回同一对象,超出范围则各建对象为 false;比较值应始终用 equals。详见《02-面向对象与核心类》

354.(并发·易)synchronized 修饰实例方法时,锁对象是___。

答案:this/当前对象/调用该方法的对象 解析:实例方法锁 this,静态方法锁 Class 对象,同步代码块锁括号里指定的对象,三种用法锁的对象各不相同。详见《01-多线程与并发编程》

355.(并发·易)调用 thread.start() 之后,线程进入的第一个状态是___(填英文状态名)。

答案:RUNNABLE 解析:start 只是让线程进入可运行状态参与调度,并不等于立刻占用 CPU;NEW 是还没 start 时的状态。详见《01-多线程与并发编程》

356.(并发·中)ReentrantLock 默认是___锁,只有在构造时传入 true 才会按队列顺序发放。

答案:非公平 解析:非公平锁允许新线程插队先 CAS 抢一次,省掉挂起与唤醒开销、吞吐更高;只有防饥饿才需要公平模式。详见《01-多线程与并发编程》

357.(并发·中)ReentrantReadWriteLock 的读锁与写锁共用一个 state 字段,其中高 16 位记录___。

答案:读锁的持有次数/读锁的数量/读锁被获取的总次数 解析:低 16 位是写锁的重入次数、高 16 位是读锁持有总数,因此读写锁的重入上限都是 65535。详见《01-多线程与并发编程》

358.(并发·难)虚拟线程在 synchronized 块中执行阻塞操作时无法从载体线程上卸载,这种现象称为___(中文术语或英文原词均可)。

答案:固定/钉住/pinning 解析:pinning 会让载体线程一起被占住,虚拟线程失去伸缩优势;官方建议把这类临界区改用 ReentrantLock。详见《01-多线程与并发编程》

359.(JVM·易)对象优先在新生代的___区分配;超过 -XX:PretenureSizeThreshold 的大对象则直接在___分配。

答案:Eden/伊甸园区;老年代 解析:Eden 是新生代的主要分配区,大对象直接进老年代可避免在 Eden 与 Survivor 之间反复复制。详见《02-JVM核心》

360.(网络·易)TCP 依靠序号+___实现丢包重传,依靠___窗口实现流量控制。

答案:确认应答/ACK;滑动 解析:ACK 与超时重传保证数据不丢,滑动窗口让发送方按接收方的处理能力调速,拥塞控制则保护网络本身。详见《03-IO模型与网络编程》

361.(操作系统·易)进程是___分配的基本单位,线程是___调度的基本单位。

答案:资源;CPU/处理器 解析:进程拥有独立地址空间等资源,线程共享进程资源但各有自己的栈与程序计数器,是调度实际作用的对象。详见《03-IO模型与网络编程》

362.(操作系统·易)虚拟地址到物理地址的转换依靠___完成;当访问的页面不在物理内存中时,会触发___中断。

答案:页表;缺页 解析:页表记录虚拟页到物理页框的映射,缺页中断由内核把页面从磁盘调入后再恢复执行,这是按需调页的基础。详见《03-IO模型与网络编程》

363.(JVM·中)Java 8 起方法区由___实现,使用本地内存;其容量上限可通过参数 -XX:___ 设置。

答案:元空间/Metaspace;MaxMetaspaceSize 解析:元空间取代了永久代,默认不设上限只受本机内存约束,动态生成类过多会 OOM: Metaspace,生产建议显式限制。详见《02-JVM核心》

364.(网络·易)HTTPS 握手阶段用___加密安全地协商出会话密钥,之后的通信数据使用___加密传输。

答案:非对称/公钥;对称 解析:非对称加密安全但慢,只用来交换密钥;对称加密快,用来承载真正的业务数据,这就是混合加密的分工。详见《03-IO模型与网络编程》

365.(MySQL·易)建表时,金额与成绩等要求精确的小数字段应使用___类型,而不是 FLOAT/DOUBLE。

答案:DECIMAL/decimal/DECIMAL(M,D)/定点数 解析:FLOAT/DOUBLE 以二进制浮点存储,比较与累加都会产生误差;DECIMAL 按十进制精确存储,代价是运算稍慢。详见《MySQL基础与SQL实战》

366.(MySQL·易)联合索引中,查询条件必须从最左列开始连续命中才能用上索引,这一规则称为___原则。

答案:最左前缀/最左匹配/最左前缀匹配 解析:(a,b,c) 相当于提供 a、(a,b)、(a,b,c) 三套索引能力,跳过 a 直接查 b 就用不上这个索引。详见《索引-事务-锁与调优》

367.(MySQL·易)InnoDB 的行锁是加在___上的,因此 UPDATE/DELETE 的 WHERE 条件若没有走索引,会退化成扫描全表并把扫过的记录全锁住。

答案:索引/索引项/索引记录 解析:这也解释了为什么加锁类语句的条件列必须有索引;无索引的 UPDATE 会把每一条记录都加上临键锁,约等于锁全表。详见《索引-事务-锁与调优》

368.(MySQL·中)explain 的 Extra 列出现___表示用到了覆盖索引、无需回表;出现___表示有额外的文件排序需要优化。

答案:Using index;Using filesort 解析:前者说明查询列都在索引中,后者说明排序无法由索引顺序满足;Using temporary(临时表)同样是值得优化的信号。详见《索引-事务-锁与调优》

369.(MySQL·中)SQL 逻辑执行顺序中,___在 GROUP BY 之前过滤行,___在分组之后过滤组。

答案:WHERE;HAVING 解析:因为 WHERE 早于 SELECT 执行,它引用不了 SELECT 中定义的别名,而 HAVING 与 ORDER BY 可以。详见《MySQL基础与SQL实战》

370.(MySQL·难)主从延迟的三类常见成因是:从库重放能力不足、主库存在___导致从库长时间追不上、从库机器配置弱于主库。

答案:大事务/大事务或大表 DDL 解析:单线程重放遇上一次性改几十万行的事务就会持续落后;对策是拆小事务、开启并行复制、关键读强制走主库。详见《索引-事务-锁与调优》

371.(Redis·易)删除百万元素的大 key 时,用___命令把键从键空间摘除、内存回收交给后台线程,可避免 DEL 造成的长时间阻塞。

答案:UNLINK/unlink 解析:大 key 的痛点是释放内存的耗时全压在主线程上;UNLINK 先摘除键、由后台线程回收内存,把阻塞降到最低。日常还应定期用 redis-cli --bigkeys 排查大 key。详见《01-Redis》

372.(Redis·易)缓存 key 即使业务上”不会变”,也建议显式设置___,它既能回收冷数据,也是删缓存失败时的一致性兜底。

答案:过期时间/TTL/ttl/EXPIRE 解析:业务数据变更时,即使删缓存的逻辑漏了或调用失败,到期后也会自动失效并重新加载,这是最后一道兜底。详见《01-Redis》

373.(消息队列·易)Kafka 中消费进度体现为消费者提交的___,重置它就能重新消费历史消息。

答案:offset/位移/偏移量 解析:Kafka 不因消费而删除消息,消费进度只是一个位移数字,所以既能被多个消费组各读各的,也能通过重置位移做数据回放。详见《03-Kafka》

374.(Redis·中)Redis Sentinel 中,单个哨兵判定主节点失联称为___下线;达到 quorum 数量的哨兵共同确认后才升级为___下线并触发故障转移。

答案:主观;客观 解析:这样设计是为了避免单个哨兵的网络抖动引发误切换,也正是哨兵要多节点部署的原因。详见《01-Redis》

375.(消息队列·中)RocketMQ 中,Broker 对迟迟收不到二次确认的半消息会发起___,由生产者反查本地事务状态后返回提交或回滚。

答案:事务回查/回查 解析:回查依据是 checkLocalTransaction() 里反查业务表(如订单是否创建成功),把”本地事务成功”与”消息可达”绑成最终一致。详见《04-RocketMQ》

376.(消息队列·难)RocketMQ 顺序消息依赖两段配合:发送时按业务 key 哈希进入同一个___,消费端用 ConsumeMode.___ 加队列锁单线程消费。

答案:MessageQueue;ORDERLY 解析:队列内天然 FIFO,加锁串行消费才能保证顺序。代价是吞吐下降、消费失败会阻塞该队列的重试,Broker 主从切换也会短暂破坏顺序。详见《04-RocketMQ》

377.(Elasticsearch·中)Elasticsearch 中在 refresh 之前就记录下写入操作、用于防止内存数据丢失的日志称为___。

答案:translog/事务日志 解析:写入同时进内存 buffer 和 translog,即使进程崩溃也能靠 translog 重放恢复;它要到 flush 时才被清空。refresh 只负责「可见」,translog 负责「不丢」。详见《05-Elasticsearch》

378.(ZooKeeper·易)ZooKeeper 中生命周期与客户端会话绑定、会话断开后由服务端自动删除的节点称为___节点。

答案:临时/ephemeral 解析:临时节点是服务注册与分布式锁自动释放的基石;在此基础上加顺序后缀得到的临时顺序节点,可实现公平锁并避免羊群效应。详见《06-ZooKeeper与Nacos》

379.(ZooKeeper·易)Nacos 临时实例默认每___秒发送一次心跳,超过___秒未收到心跳的实例会被自动剔除。

答案:5;30 解析:15 秒未收到心跳只是标记为不健康,30 秒未收到才真正剔除。这套「心跳保活 + 超时剔除」的机制正是 AP 模式下最终一致的实现方式。详见《06-ZooKeeper与Nacos》

380.(Nginx·易)Nginx 配置中的___指令块用于定义一组后端服务器,并按指定策略把请求分发到它们。

答案:upstream 解析:upstream 定义节点池,配合 weight(权重轮询)、ip_hash(会话保持)、least_conn(最少连接)等策略;location 里再用 proxy_pass 引用它,这是负载均衡配置的标准结构。详见《07-Nginx》

381.(MongoDB·中)MongoDB 中让文档在指定秒数后自动过期的索引称为___索引。

答案:TTL 解析:db.log_events.createIndex({ createdAt: 1 }, { expireAfterSeconds: 3600 }) 即可让文档在 createdAt 之后一小时自动删除,特别适合日志、埋点、临时会话这类「只关心近期数据」的场景,省去手写清理任务。详见《08-MongoDB》

382.(框架·易)Spring 中声明式事务依赖的注解是 @___。

答案:Transactional 解析:加到方法或类上即可,底层由 AOP 代理实现,所以一切绕过代理的调用(自调用、非 public 方法)都会失效。详见《Spring核心》

383.(框架·易)Spring 中 @Autowired 默认按___匹配候选 Bean,候选不唯一时可用 @Qualifier 按名称指定。

答案:类型 解析:@Autowired 是 Spring 提供的注解、默认按类型注入,配合 @Qualifier 或 @Primary 解决多候选;@Resource 是 JDK 规范,默认按名称。详见《Spring核心》

384.(框架·易)MyBatis 中用于遍历集合、生成 IN 查询或批量插入的动态 SQL 标签是 <___>。

答案:foreach 解析:foreach 负责循环,collection 指定集合、item 是元素别名、separator 是分隔符;配合 <where> 的”去头”和 <set> 的”去尾”构成动态 SQL 的常用组合。详见《MyBatis》

385.(单元测试·易)JUnit 5 中标注在方法上、表示每个 @Test 执行前都会先跑一次的注解是 @___。

答案:BeforeEach 解析:@BeforeEach/@AfterEach 每个用例各一次,@BeforeAll/@AfterAll 全部用例共一次且必须是 static 方法——因为 JUnit 5 为每个 @Test 都新建一个测试类实例。详见《05-单元测试与工程质量》

386.(单元测试·中)Mockito 中要断言”某个方法从未被调用过”,应写成 verify(mock, ___())。

答案:never 解析:verify 的默认次数是 times(1),还有 atLeastOnce()/atMost();用 never() 断言”不该发生的事”是单测里容易被忽略、价值却很高的一类断言。详见《05-单元测试与工程质量》

387.(认证与安全·易)RBAC 模型中,用户不直接关联权限,而是通过___作为中转。

答案:角色 解析:链路是用户 → 角色 → 权限 → 资源,角色是批量授权的中转站,换岗只改角色不改人;落地时前端按钮显隐只是体验,后端接口鉴权才是安全。详见《06-认证与安全》

388.(分布式与高并发·易)BASE 理论是对 CAP 中 AP 路线的工程化延伸,三个字母分别指基本可用、软状态和___。

答案:最终一致/Eventually Consistent/最终一致性 解析:Basically Available 指降级、限流保住核心功能;Soft state 指允许”支付处理中”这类中间态存在;Eventually consistent 指经过一段时间同步与补偿后收敛到一致。互联网业务的主流选择就是”接受短暂不一致,用对账与补偿把窗口压到最小”。详见《01-分布式理论基础》

389.(分布式与高并发·中)Seata AT 模式在一阶段把业务 SQL 修改前后的镜像写入 ___ 表,并与业务数据在同一个本地事务中提交,二阶段回滚时据此生成反向 SQL。

答案:undo_log 解析:前后镜像(before/after image)随本地事务一起落库,保证”业务改动”与”可回滚信息”原子;全局提交时只需异步批量删除,几乎无性能损耗;全局回滚时先校验后镜像没有被别人改过,再用前镜像生成补偿 SQL。AT 靠 TC 的全局锁保证回滚窗口内数据不被其他全局事务修改,用并发性能换业务零侵入(一个 @GlobalTransactional 注解)。详见《01-分布式理论基础》

390.(系统设计·易)短链系统通常用自增 ID 配合 ___ 编码(0-9、A-Z、a-z 共 62 个字符)生成 6~7 位短码。

答案:Base62 解析:62 进制把长数字压得很短,6 位就有 62^6 ≈ 568 亿的空间,配合发号器(DB 号段 / Redis INCR / 雪花)即可。自定义后缀则另走业务表的唯一键,冲突就重试。别忘了跳转用 301、不存在的短码用布隆过滤器提前挡掉。详见《03-系统设计场景题》

391.(系统设计·中)统计 UV 时只关心去重后的数量、不关心具体是谁,用 Redis 的 ___ 结构只需约 12KB 就能把标准误差控制在 0.81%。

答案:HyperLogLog 解析:HyperLogLog 用概率算法估算基数,内存恒定约 12KB(16384 个 6bit 寄存器),比 SET 省下几个数量级。代价是只能拿到计数、拿不到成员列表,且结果本身是近似值。判断要不要精确应先问指标的精度要求:排行榜要精确名次,UV 通常只用来看趋势。详见《01-Redis》

392.(系统设计·中)IM 系统为每个会话维护一个单调递增的 ___,接收方按它排序与去重,一次解决”不乱序”和”重复消息”两个问题。

答案:seq/序列号/会话内序列号 解析:IM 的可靠性四件套是不丢(ACK + 重传 + 离线拉取)、不乱(会话内单调递增的 seq)、不重(幂等 msgId)、在线状态(心跳 / 临时节点)。有了 seq,客户端断线重连只需上报”我收到的最大 seq”,服务端就能续传缺口,比逐条确认高效得多;已读回执也别逐条存,用位图或区间压缩。详见《03-系统设计场景题》

393.(工具链·中)打 war 包部署到外部 Tomcat 时,spring-boot-starter-tomcat 应设为 <scope>___</scope>:编译与测试可用,但不打进包,由运行容器提供。

答案:provided 解析:provided 在编译、测试 classpath 上有效而运行 classpath 上无效;若不这样设,war 内嵌的 Tomcat 会和外部容器自带的冲突导致启动失败。打可执行 jar 独立运行的场景则不需要。详见《04-开发工具链-Git-Maven-Linux》

394.(工具链·中)统计 access.log 中访问次数 Top10 的 IP,命令为 awk '{print $1}' access.log | sort | ___ | sort -rn | head -10。

答案:uniq -c 解析:uniq 只能合并相邻的重复行,所以必须先 sort;-c 给每组加上出现次数前缀,再 sort -rn 按次数降序取前 10。这也解释了为什么不能直接对原始文件 sort -rn——那时比较的是整行文本而不是计数。详见《04-开发工具链-Git-Maven-Linux》

395.(设计模式·易)Integer 缓存 -128~127 与字符串常量池复用字面量,都是___模式的落地:由工厂统一管理、共享不可变实例来节省内存。

答案:享元 解析:共享的是”多个相同值的对象共用同一份实例”,而不是”全局唯一实例”,所以不是单例;这也解释了 Integer a=128,b=128 时 == 为 false——超出缓存范围就不再共享。享元对象必须不可变,否则一个持有者改了全都受影响。详见《07-设计模式》

396.(设计模式·中)静态内部类(Holder)单例既能延迟加载又线程安全,靠的是内部类被首次引用时才触发类加载,且 JVM 保证同一个类的___过程只执行一次。

答案:初始化/类初始化 解析:类初始化由 JVM 加锁完成,所以 Holder.I 只会被创建一次,既不用 volatile 也不用显式加锁;延迟则来自”内部类只有被引用时才加载”这一特性。详见《07-设计模式》

397.(算法·易)二分查找中,为避免 (left + right) / 2 在两个大整数相加时溢出,常写成 mid = left + ___。

答案:(right - left) / 2 解析:变形后等价于取中间位置,差值不会超过 int 范围;(left + right) >>> 1 也是常见的无溢出写法。详见《03-算法刷题路线》

398.(算法·中)「找下一个更大元素」这一类题(每日温度、柱状图中最大的矩形)的标准解法是维护一个___栈。

答案:单调栈/单调递减栈 解析:把暂时还没有更大元素的下标压栈,遇到更大的就弹栈结算,每个元素最多进出栈一次,整体 O(n);与暴力双重循环的 O(n²) 形成对比。详见《03-算法刷题路线》

判断题

399.(并发·易)volatile 可以保证 i++ 这类复合操作的原子性。

  • A. 正确
  • B. 错误

答案:B 解析:volatile 只管可见性与有序性,i++ 是”读-改-写”三步,并发下仍会丢更新,需要 synchronized 或 AtomicInteger。详见《01-多线程与并发编程》

400.(集合·易)HashMap 的 key 允许为 null。

  • A. 正确
  • B. 错误

答案:A 解析:JDK 1.8 允许一个 null 键(固定放在 0 号桶);而 Hashtable 与 ConcurrentHashMap 的 key、value 都不允许为 null。详见《03-集合与泛型》

401.(Java基础·中)"a|b".split("|") 能正确按竖线切成两个元素,不需要转义。

  • A. 正确
  • B. 错误

答案:B 解析:split 的参数是正则,竖线表示”或”,结果是每个字符被空匹配切开;必须写成 split(”\|”)。详见《02-面向对象与核心类》

402.(并发·中)synchronized 修饰静态方法时,锁的是该类的 Class 对象。

  • A. 正确
  • B. 错误

答案:A 解析:静态方法没有 this,锁的是 Class 对象,与实例锁互不影响——同一类里静态与实例同步方法可以并发进入。详见《01-多线程与并发编程》

403.(集合·中)重写 equals 而不重写 hashCode,程序一定会抛出异常或明显出错。

  • A. 正确
  • B. 错误

答案:B 解析:不放进哈希容器时逻辑上看不出问题,所以更隐蔽;一旦用作 HashMap 键或 HashSet 元素,就会重复存、get 不到。违反的是约定而非必然崩溃。详见《02-面向对象与核心类》

404.(框架·易)MyBatis 的 #{} 最终会被编译成 JDBC 的预编译占位符 ?。

  • A. 正确
  • B. 错误

答案:A 解析:#{} 由 PreparedStatement 用 setXxx 绑定参数,SQL 结构在编译期确定,因此能防注入。详见《MyBatis》

405.(Redis·中)Redis 6.0 的多线程只用于网络 IO 与协议解析,命令的执行仍然是单线程。

  • A. 正确
  • B. 错误

答案:A 解析:多线程解决的是网络读写瓶颈;命令在单线程中串行执行,因此单条命令的原子性依旧成立,Lua 脚本也据此工作。详见《01-Redis》

406.(消息队列·中)Kafka Topic 增加分区数后,同一个 key 的消息可能被路由到与历史不同的分区。

  • A. 正确
  • B. 错误

答案:A 解析:分区路由是 hash(key) % 分区数,分区数一变映射就变;需要严格顺序的业务要评估扩容影响或改用新 Topic。详见《03-Kafka》

407.(MySQL·中)在默认 RR 隔离级别下,MySQL 已经彻底杜绝了幻读,任何写法都不会出现。

  • A. 正确
  • B. 错误

答案:B 解析:快照读靠 MVCC、当前读靠临键锁各自避免幻读,但”先快照读、再被别的事务插入、再当前读/更新”这类混用场景仍会看到幻影行。详见《索引-事务-锁与调优》

408.(框架·中)Spring 事务传播行为 REQUIRED 表示:当前有事务就加入,没有就新建一个。

  • A. 正确
  • B. 错误

答案:A 解析:这是默认值;REQUIRES_NEW 会挂起当前事务另开一个,NESTED 用保存点,SUPPORTS 无事务则非事务执行。详见《Spring核心》

409.(框架·中)@Transactional 默认会对所有抛出的 Exception(含受检异常)回滚。

  • A. 正确
  • B. 错误

答案:B 解析:默认只对 RuntimeException 和 Error 回滚,受检异常需要 rollbackFor = Exception.class 才回滚——这是最常见的”事务没回滚”原因。详见《Spring核心》

410.(单元测试·中)为提高覆盖率,单元测试应优先直接测试私有方法。

  • A. 正确
  • B. 错误

答案:B 解析:私有方法是实现细节,直接反射测试会让重构寸步难行;应通过公共入口的行为测试间接覆盖,若难测则说明该方法该拆出去了。详见《05-单元测试与工程质量》

411.(Java基础·中)Java 中 String 是基本数据类型。

  • A. 正确
  • B. 错误

答案:B 解析:String 是引用类型(final 类),8 种基本类型不含它;面试常拿它与基本类型的包装类一起考。详见《02-面向对象与核心类》

412.(集合·中)HashMap 是线程安全的。

  • A. 正确
  • B. 错误

答案:B 解析:HashMap 非线程安全,并发 put 可能丢数据甚至 1.7 下成环;并发场景用 ConcurrentHashMap 或 Collections.synchronizedMap。详见《03-集合与泛型》

413.(Java基础·中)重写 equals 必须同时重写 hashCode。

  • A. 正确
  • B. 错误

答案:A 解析:约定是相等的对象必须有相同哈希值,否则 HashMap/HashSet 中会「存进去却找不到」;反之 hashCode 相同不要求 equals 相等。详见《02-面向对象与核心类》

414.(集合·易)ArrayList 支持随机访问,按下标取元素的时间复杂度是 O(1)。

  • A. 正确
  • B. 错误

答案:A 解析:底层数组可直接寻址;LinkedList 按下标访问需从头遍历是 O(n),这也是两者选型的核心差异之一。详见《03-集合与泛型》

415.(Java8·中)Stream 可以被重复消费,同一流可以 collect 两次。

  • A. 正确
  • B. 错误

答案:B 解析:Stream 是一次性的,终端操作后流已关闭,复用会抛 IllegalStateException;需要复用就每次从源重新 stream()。详见《05-Lambda-Stream与Java8新特性》

416.(Java基础·难)Java 中 i++ 是原子操作。

  • A. 正确
  • B. 错误

答案:B 解析:i++ 是读-改-写三步,多线程下会丢更新,需 AtomicInteger 或加锁;这与 volatile 无关,volatile 也不保证原子性。详见《01-多线程与并发编程》

417.(并发·易)线程已经 start() 之后,仍然可以调用 setDaemon(true) 把它改成守护线程。

  • A. 正确
  • B. 错误

答案:B 解析:setDaemon 必须在 start 之前调用,否则抛 IllegalThreadStateException;守护属性不允许事后再改。详见《01-多线程与并发编程》

418.(并发·中)CyclicBarrier 的计数归零后会自动重置、可以循环使用,而 CountDownLatch 归零后无法重置。

  • A. 正确
  • B. 错误

答案:A 解析:CyclicBarrier 可复用且支持到齐回调,CountDownLatch 是一次性的,想重新计数只能新建对象,这是两者最常被问的区别。详见《01-多线程与并发编程》

419.(并发·中)在 synchronized 代码块内调用 Thread.sleep() 会让当前线程释放它持有的锁。

  • A. 正确
  • B. 错误

答案:B 解析:sleep 不释放锁,会抱着锁睡到超时;会释放锁的是 Object.wait(),它同时把线程挂到等待队列。详见《01-多线程与并发编程》

420.(并发·难)线程 A 先执行 v = 1(v 为 volatile),紧接着执行 y = 2(y 为普通变量);线程 B 读到 v == 1 后再读 y,则 B 一定能读到 y == 2。

  • A. 正确
  • B. 错误

答案:A 解析:A 中 v 的写先于 y 的写,volatile 写又 happens-before 读到它的读,加上 B 中读 v 先于读 y,传递性让 y 的写对 B 可见。详见《01-多线程与并发编程》

421.(JVM·易)把 -Xms 与 -Xmx 设为相同值,可以避免堆在运行期间反复扩容与收缩带来抖动,是生产的常用做法。

  • A. 正确
  • B. 错误

答案:A 解析:固定堆大小省去动态调整的代价,也便于按最坏情况规划内存,代价是启动即占用全部堆空间。详见《02-JVM核心》

422.(网络·中)TCP 是面向字节流的协议,不保留应用层的消息边界,因此粘包与拆包需要应用层自己处理。

  • A. 正确
  • B. 错误

答案:A 解析:TCP 只保证字节有序到达,会把多次 write 合并或拆分;常见解法是定长、分隔符或”长度字段+内容”的自定义协议。详见《03-IO模型与网络编程》

423.(网络·中)TCP 三次握手中,若客户端发出的第三个 ACK 报文丢失,连接一定无法建立。

  • A. 正确
  • B. 错误

答案:B 解析:客户端发出 ACK 后即认为连接已建立并可发送数据;服务端超时会重传 SYN+ACK,客户端再次确认后连接照样建立。详见《03-IO模型与网络编程》

424.(操作系统·中)只要系统的资源足够充足,就不会发生死锁。

  • A. 正确
  • B. 错误

答案:B 解析:死锁源于四个必要条件同时成立,与资源总量无关;两个线程各持一把锁再互相申请,资源再多也会死锁。详见《03-IO模型与网络编程》

425.(JVM·中)Eden 区被填满时会触发 Full GC。

  • A. 正确
  • B. 错误

答案:B 解析:Eden 满触发的是 Minor GC(Young GC);Full GC 的常见触发点老年代或元空间不足、分配担保失败、调用 System.gc() 等。详见《02-JVM核心》

426.(MySQL·易)只要在 (a, b) 上建了联合索引,单独用 b 作为查询条件也能走上这个索引。

  • A. 正确
  • B. 错误

答案:B 解析:联合索引先按 a 排序,a 未限定时 b 在全局是无序的,不满足最左前缀;需要单独给 b 建索引或调整索引列顺序。详见《索引-事务-锁与调优》

427.(MySQL·中)事务 ACID 中的隔离性(I)是靠 redo log 实现的。

  • A. 正确
  • B. 错误

答案:B 解析:隔离性靠 MVCC 与锁共同实现;redo log 保证的是持久性(D),undo log 保证的是原子性(A)。详见《索引-事务-锁与调优》

428.(MySQL·中)在大表上查询要返回其中大部分数据时,优化器主动放弃索引改为全表扫描是正常现象,此时 explain 的 key 为 NULL 未必说明 SQL 有问题。

  • A. 正确
  • B. 错误

答案:A 解析:走二级索引要逐行回表且是随机 IO,当条件区分度低、命中行数占比过高时,顺序全表扫描反而更快;一切以实测耗时为准。详见《索引-事务-锁与调优》

429.(MySQL·难)binlog 组提交(group commit)把多个事务的 binlog 刷盘合并成一次 fsync,从而摊薄两阶段提交带来的磁盘 IO 开销。

  • A. 正确
  • B. 错误

答案:A 解析:commit 阶段被拆成 flush、sync、commit 三段,每段一个队列由 leader 带队执行,多个事务共用一次刷盘;prepare 阶段不变。详见《索引-事务-锁与调优》

430.(Redis·中)Redis 的发布订阅可以当作消息队列使用,订阅者离线期间的消息会在重连后补投。

  • A. 正确
  • B. 错误

答案:B 解析:Pub/Sub 是即发即弃:没有持久化、没有 ACK、没有重投,订阅者离线期间的消息直接丢失,只适合”丢一两次无大碍”的缓存失效广播;业务消息(订单、支付)必须交给 RabbitMQ/Kafka/RocketMQ 这类专业 MQ。详见《01-Redis》

431.(消息队列·中)Kafka 通过分区设计保证了消息的全局有序,只要 Topic 有足够多的分区,就能同时兼顾吞吐与全局顺序。

  • A. 正确
  • B. 错误

答案:B 解析:Kafka 只保证分区内有序,分区之间没有任何顺序关系,所以”全局有序”与”多分区并行”本质互斥,真要全局有序只能退化成单分区。业务上的常规解法是收敛为”同一业务 key 有序”,按 key 路由到同一分区。详见《03-Kafka》

432.(Redis·中)RDB 是二进制的紧凑快照,恢复速度比 AOF 快,代价是可能丢失最后一次快照之后的写入。

  • A. 正确
  • B. 错误

答案:A 解析:RDB 定时 fork 子进程做全量快照,文件小、加载快,但两次快照之间的写入会丢;AOF 记录写命令序列,everysec 下最多丢约 1 秒,代价是文件更大、恢复更慢。生产常用混合持久化兼顾两者。详见《01-Redis》

433.(消息队列·易)RabbitMQ 生产者开启 publisher confirm 后收到 Basic.Ack,只说明消息已被 Broker 接收,并不代表消费者已经处理成功。

  • A. 正确
  • B. 错误

答案:A 解析:Confirm 只覆盖”生产者到 Broker”这一段,与消费成功与否无关;若消息到了交换机却路由不到任何队列,还要靠 publisher-returns + ReturnCallback 才能捕获。全链路可靠需要队列与消息持久化、消费者手动 ACK、失败重试与死信队列逐段补齐。详见《02-RabbitMQ》

434.(Elasticsearch·中)ES 中「更新一条文档」的底层实现是直接在原 segment 里原地修改这条记录。

  • A. 正确
  • B. 错误

答案:B 解析:segment 一旦生成即不可修改,更新实际是「标记旧文档为删除 + 写入一条新文档」,被标记的旧文档要到段合并(merge)时才真正回收空间——这正是 ES 更新代价高的根源,也是它不适合当主库的原因之一。详见《05-Elasticsearch》

435.(Nginx·中)开源版 Nginx 的 upstream 只提供被动健康检查(max_fails/fail_timeout),主动探测需要商业版或第三方模块。

  • A. 正确
  • B. 错误

答案:A 解析:被动检查是「请求失败达到 max_fails 次就把该节点暂停 fail_timeout 时长不派流量」,属于事后发现;主动探测(定期发请求探活)开源版不具备,需要商业版或 OpenResty 等第三方模块补齐。详见《07-Nginx》

436.(MongoDB·易)MongoDB 的单文档更新本身就是原子的,所以把强关联数据放进同一个文档、减少对跨文档事务的依赖,是它的惯用设计。

  • A. 正确
  • B. 错误

答案:A 解析:一条 BSON 文档的更新不会「写一半」,因此「把需要一起变更的数据内嵌在同一文档里」比开事务更高效,这也是文档模型的核心设计思路。MongoDB 4.0 起虽支持多文档事务,但性能与生态仍不如关系库,不适合当 MySQL 的替代品。详见《08-MongoDB》

437.(框架·中)Spring Boot 扫描到的候选自动配置类都会被注册成 Bean,条件注解只决定它们的加载先后顺序。

  • A. 正确
  • B. 错误

答案:B 解析:条件注解是”是否生效”的开关,不满足条件的候选类根本不会注册(@ConditionalOnMissingBean 就是让位给用户配置的关键);加载先后顺序由 @AutoConfigureOrder/@AutoConfigureBefore 这类注解控制,与条件注解是两件事。详见《SpringBoot实战》

438.(框架·难)Feign 的重试只应对幂等操作开启:对下单这类非幂等接口重试,可能造成重复下单。

  • A. 正确
  • B. 错误

答案:A 解析:重试的本质是”再发一次请求”,查询接口重复执行无害,下单、扣款重复执行就是事故;所以只给幂等接口配 Retryer,非幂等接口要靠超时 + 幂等号来兜底。另需显式配置 connectTimeout/readTimeout,避免下游慢调用拖垮自己。详见《SpringCloud微服务》

439.(框架·难)Sentinel 触发限流或熔断后,请求会走 blockHandler 等兜底逻辑返回降级结果,而不是把异常直接抛给用户。

  • A. 正确
  • B. 错误

答案:A 解析:这正是”降级”的意义:被限流/熔断/异常时返回缓存旧值、默认值这类”有损但可用”的结果。注意规则默认存在内存里,重启即丢失,生产要接 Nacos 做规则持久化。详见《SpringCloud微服务》

440.(单元测试·难)测试类上标注 @Transactional 后,用例结束时数据库、Redis 与消息队列的写入都会一起回滚,因此不会污染环境。

  • A. 正确
  • B. 错误

答案:B 解析:它只回滚当前事务里的数据库操作,回滚不了 Redis、消息队列等外部副作用;而且因为复用同一连接、内层事务被合并,还会掩盖事务传播行为的 bug。生产级做法是独立测试库每轮清理,或用 Testcontainers 每次起新容器。详见《05-单元测试与工程质量》

441.(认证与安全·中)JWT 的 payload 是加密的,所以把手机号等敏感信息放进 payload 是安全的。

  • A. 正确
  • B. 错误

答案:B 解析:JWT 是”签名”不是”加密”,header 与 payload 只是 Base64URL 编码,任何人都能解开看内容;签名只保证不被篡改,不保证保密。payload 里只放 userId、role 这类不敏感标识,手机号等敏感数据留在服务端按需查询。详见《06-认证与安全》

442.(分布式与高并发·中)一致性哈希环上移除一个节点时,该节点原本承载的数据会被平均分摊到其余所有节点上。

  • A. 正确
  • B. 错误

答案:B 解析:数据是沿环顺时针找到第一个节点存放的,所以被移除节点的数据只会整体交给它在环上的顺时针后继那一台,其余节点分毫不动。之所以看起来”分摊开了”,是因为虚拟节点让每个物理节点在环上有上百个落点,它的后继自然散布在多台机器上——这是虚拟节点的第二个好处:故障压力均摊。详见《01-分布式理论基础》

443.(系统设计·易)短链跳转返回 301(永久重定向)而不是 302,好处是浏览器会缓存这次跳转,从而减少对短链服务的回源请求。

  • A. 正确
  • B. 错误

答案:A 解析:301 会被浏览器缓存,同一短码的后续访问可能根本不打到服务端,对读多写少的短链系统是很大的减负。反过来,如果业务强依赖每次点击都回源,才会考虑 302——但点击统计的正确做法是异步上报(写 MQ 不阻塞跳转),而不是牺牲缓存来换。详见《03-系统设计场景题》

444.(系统设计·中)Redis ZSET 中分数相同的成员会按成员名的字典序排列,因此做排行榜时若要求”同分先达到者排前”,需要把时间信息编进 score。

  • A. 正确
  • B. 错误

答案:A 解析:ZSET 的排序规则是”先比 score,score 相同再按 member 字典序”,字典序与达成先后毫无关系。常规做法是把 score 设计成复合值(例如 分数 × 10^10 + (大常数 - 时间戳)),一次 ZADD 就把并列名次也定好;取榜单用 ZREVRANGE,查自己名次用 ZREVRANK。注意”精确名次”与”TopN 列表”是两个量级的问题,亿级数据要靠分桶 + 近似 + 定时快照。详见《03-系统设计场景题》

445.(工具链·易)git rebase 会把被重放的提交彻底删除,之后再也找不回来。

  • A. 正确
  • B. 错误

答案:B 解析:rebase 是”把提交逐个重放”,原提交仍以悬空对象留在仓库里,通过 reflog 或旧 sha 通常还能找回,直到 gc 回收;它真正危险的地方不是”丢内容”,而是改写了公共历史导致协作者本地历史错位。详见《04-开发工具链-Git-Maven-Linux》

446.(设计模式·中)把 @Scope("prototype") 的 Bean 注入到一个单例 Bean 之后,该单例持有的始终是注入时创建的那一个实例。

  • A. 正确
  • B. 错误

答案:A 解析:注入只发生一次,之后这个字段一直指向最初创建的对象。想每次都拿新的,需要用 ObjectProvider、@Lookup 或直接从容器 getBean——这是原型模式在 Spring 里最常见的踩坑点。详见《07-设计模式》

447.(算法·易)快速排序的最坏时间复杂度是 O(nlogn)。

  • A. 正确
  • B. 错误

答案:B 解析:快排最坏是 O(n²)——每次划分只能分出一个元素(如已有序数组固定取首元素为基准),递归深度退化成 n。O(nlogn) 是它的平均复杂度,这一点是最常见的时间复杂度误解之一。详见《03-算法刷题路线》

448.(算法·中)HashMap 的查询平均时间复杂度是 O(1),最坏情况下会退化成 O(n)。

  • A. 正确
  • B. 错误

答案:A 解析:哈希均匀时定位到桶是常数时间;大量 key 落在同一个桶(哈希碰撞)时会退化成链表扫描,JDK 1.8 用”链表长度超过 8 且容量 ≥ 64 时转红黑树”把最坏情况改善到 O(logn)。详见《03-集合与泛型》