Java 热门面试题 200 道(Markdown表格版)【简化版】
Java与数据库核心面试题摘要
本文精选200道Java与数据库高频面试题,重点涵盖:
Java集合: HashMap原理(数组+链表/红黑树)、ConcurrentHashMap分段锁优化、红黑树改进目的(解决哈希冲突性能问题)
MySQL索引: 最左前缀原则、覆盖索引(避免回表)、B+树优势(矮胖结构适合磁盘IO)、聚簇/非聚簇索引区别
事务隔离: 脏读、不可重复读、幻读的典型场景
消息队列: RabbitMQ死信队列触发条件(消息拒收/TTL过期/队列满)、延迟队列实现方案
性能优化: 索引失效场景(函数操作/类型转换)、索引数量权衡(写性能与空间开销)
典型问题示例: MySQL索引下推(ICP)在引擎层提前过滤数据,减少回表次数;RocketMQ选用轻量级NameServer替代ZK以提升性能。
题目列表
No. | title | answer | tagList |
---|---|---|---|
1 | 说说 Java 中 HashMap 的原理? | HashMap 基于哈希表实现,使用数组+链表/红黑树存储数据。通过 key 的 hashCode 计算桶位置(数组索引),发生哈希冲突时采用链地址法解决(JDK1.8 后链表长度≥8时转为红黑树)。允许 null 键/值,非线程安全。扩容时容量翻倍并重新哈希。 | Java,Java 基础 |
2 | Java 中 ConcurrentHashMap 1.7 和 1.8 之间有哪些区别? | 1.7 使用分段锁(Segment),每个段独立加锁;1.8 改为 CAS + synchronized 锁单个桶(Node)。1.7 只有链表,1.8 引入红黑树优化长链表查询。1.8 的 size() 计算改用 baseCount + CounterCell 避免全局锁。 | Java 集合,Java |
3 | 为什么 JDK 1.8 对 HashMap 进行了红黑树的改动? | 解决哈希冲突严重时链表过长导致的查询效率退化(O(n) 降至 O(logn))。当链表长度 ≥8 且数组容量 ≥64 时转为红黑树;节点数 ≤6 时退化为链表。平衡空间与时间开销。 | Java 集合,Java |
4 | JDK 1.8 对 HashMap 除了红黑树还进行了哪些改动? | 1. 头插法改为尾插法(避免多线程扩容死循环) 2. 扩容时旧链表拆分为高低位链表(优化重新哈希计算) 3. 计算哈希值:高位参与运算 (h = key.hashCode()) ^ (h >>> 16) 4. 初始化延迟到首次 put 时。 | Java 集合,Java |
5 | Java 中有哪些集合类?请简单介绍 | List:ArrayList(数组)、LinkedList(双向链表)、Vector(线程安全数组) Set:HashSet(基于HashMap)、TreeSet(红黑树)、LinkedHashSet(链表保序) Map:HashMap、TreeMap(键有序)、LinkedHashMap(插入序)、ConcurrentHashMap(线程安全) Queue:ArrayDeque、PriorityQueue(堆) | Java 集合,Java |
6 | MySQL 索引的最左前缀匹配原则是什么? | 联合索引 (a,b,c) 生效条件:查询必须包含最左列 a。能匹配 a、a,b、a,b,c 的组合,但无法跳过 a 直接查 b,c。原理:索引按列顺序存储,缺失左列时无法利用有序性。 | 后端,MySQL,数据库 |
7 | 数据库的脏读、不可重复读和幻读分别是什么? | 脏读:读到未提交的数据(事务A读事务B未提交修改) 不可重复读:同事务内两次读同一数据结果不同(因其他事务修改) 幻读:同事务内两次查询结果集数量不同(因其他事务增删数据)。 | 后端,MySQL,数据库 |
8 | MySQL 的存储引擎有哪些?它们之间有什么区别? | InnoDB:支持事务、行锁、外键、聚簇索引,适用OLTP MyISAM:表锁、全文索引、高读性能,不支持事务,适用OLAP Memory:内存存储,数据易丢失 区别核心:事务支持(InnoDB支持)、锁粒度(InnoDB行锁 vs MyISAM表锁)、崩溃恢复能力。 | 后端,MySQL,数据库 |
9 | MySQL 的覆盖索引是什么? | 索引包含查询所需所有字段(如 SELECT a,b FROM t WHERE c=1,索引 (c,a,b))。避免回表查询(直接从索引取数据),显著提升性能。EXPLAIN 显示 “Using index” 即使用了覆盖索引。 | 后端,MySQL,数据库 |
10 | MySQL 的索引类型有哪些? | 1. 主键索引(唯一 + 非空) 2. 唯一索引(列值唯一) 3. 普通索引(加速查询) 4. 全文索引(FULLTEXT,文本搜索) 5. 组合索引(多列联合) 6. 空间索引(GIS 数据)。 | 后端,MySQL,数据库 |
11 | MySQL 的索引下推是什么? | ICP(Index Condition Pushdown):在存储引擎层提前过滤索引条件(即使非最左前缀)。例如索引 (a,b),查询 WHERE a=1 AND b>2,引擎层直接过滤 b>2,减少回表次数。需满足条件:range/ref/eq_ref 查询,且存储引擎支持(默认开启)。 | 后端,MySQL,数据库 |
12 | MySQL InnoDB 引擎中的聚簇索引和非聚簇索引有什么区别? | 聚簇索引:叶子节点存数据行(主键索引即聚簇索引) 非聚簇索引(二级索引):叶子节点存主键值,需回表查询数据 区别:1. 数据存储位置(聚簇索引与数据共存) 2. 查询效率(聚簇索引无需回表) 3. 数量限制(每表仅一个聚簇索引)。 | 后端,MySQL,数据库 |
13 | MySQL 中的回表是什么? | 通过二级索引查询时,需先查索引找到主键值,再根据主键到聚簇索引中取完整数据行。额外 IO 操作导致性能下降。避免方式:使用覆盖索引或优化索引设计。 | 后端,MySQL,数据库 |
14 | MySQL 中使用索引一定有效吗?如何排查索引效果? | 不一定有效。失效场景:函数操作索引列、类型隐式转换、OR 条件未全索引、LIKE 以通配符开头、不符合最左前缀。排查:1. EXPLAIN 看 type/possible_keys/key 2. 观察 rows 字段预估扫描行数 3. 开启慢查询日志分析。 | 后端,MySQL,数据库,场景题 |
15 | RabbitMQ 怎么实现延迟队列? | 两种方式: 1. 死信队列(DLX):消息设置 TTL 过期后转投 DLX 2. 插件(rabbitmq_delayed_message_exchange):使用 x-delayed-type 交换机,消息头设置 x-delay 参数(毫秒)。推荐插件方式避免 TTL 队列阻塞问题。 | 后端,消息队列,RabbitMQ |
16 | MySQL 中的索引数量是否越多越好?为什么? | 不是。原因:1. 写性能下降(增删改需维护所有索引) 2. 空间占用增加 3. 优化器选择困难可能选错索引。建议:根据查询需求创建必要索引,优先组合索引覆盖多查询,定期分析索引使用率(SHOW INDEX 观察 Cardinality)。 | 后端,MySQL,数据库 |
17 | 为什么 RocketMQ 不使用 Zookeeper 而选择自己实现 NameServer? | 1. 轻量化:NameServer 无选举逻辑(AP 系统),ZK 是 CP 系统较重 2. 性能:NameServer 纯内存操作,吞吐更高 3. 去中心化:各 NameServer 节点独立无状态,避免 ZK 集群瓶颈 4. RocketMQ 只需简单服务发现,无需 ZK 强一致性。 | RocketMQ,消息队列,后端 |
18 | 请详细描述 MySQL 的 B+ 树中查询数据的全过程 | 1. 从根节点开始二分查找 2. 沿非叶子节点(仅存索引键 + 指针)逐层向下定位 3. 到达叶子节点(存数据行或主键) 4. 在叶子节点链表二分查找目标键 5. 若聚簇索引直接返回数据;若二级索引则取主键回表查询。 | 后端,MySQL,数据库 |
19 | RabbitMQ 中消息什么时候会进入死信交换机? | 触发条件: 1. 消息被消费端拒绝(basic.reject/nack)且 requeue=false 2. 消息 TTL 过期 3. 队列达到最大长度(溢出丢弃)。需配置队列的 x-dead-letter-exchange 参数绑定死信交换机。 | RabbitMQ,消息队列,后端 |
20 | 为什么 MySQL 选择使用 B+ 树作为索引结构? | 对比 B 树: 1. 更矮胖:非叶子节点不存数据,单节点存更多键,减少 IO 次数 2. 范围查询高效:叶子节点双向链表串联 3. 查询稳定:所有查询均需到叶子节点,时间复杂度稳定 O(log n) 4. 更适合磁盘存储:节点大小匹配磁盘页。 | 后端,MySQL,数据库 |
21 | RabbitMQ 中无法路由的消息会去到哪里? | 取决于 Mandatory 参数: 1. Mandatory=true:通过回调 ReturnListener 返回生产者 2. Mandatory=false(默认):直接丢弃。建议:设置备份交换机(Alternate Exchange)接收无法路由的消息。 | RabbitMQ,消息队列,后端 |
22 | MySQL 三层 B+ 树能存多少数据? | 假设: - 页大小 16KB - 主键 BIGINT(8B),指针 6B - 非叶节点每页存约 16KB/(8B+6B)≈1170 键 - 叶节点存数据行(假设1KB/行),每页约16行 计算:根节点有 1170 指针 → 二层 1170 页 → 三层 1170×1170≈137 万页 → 总行数 ≈ 137万 × 16 = 2190 万行。 | 后端,MySQL,数据库 |
23 | Kafka为什么要抛弃 Zookeeper? | 1. 简化架构:减少外部依赖,运维更简单 2. 提升扩展性:元数据管理内置(KIP-500),突破 ZK 集群写瓶颈 3. 增强稳定性:避免 ZK 故障导致 Kafka 不可用 4. 改进控制器选举:用 Raft 协议替代 ZK 监听。从 Kafka 2.8 起支持 KRaft 模式。 | Kafka,Zookeeper,消息队列 |
24 | 详细描述一条 SQL 语句在 MySQL 中的执行过程。 | 1. 连接器:建立连接,权限验证 2. 查询缓存:若开启缓存且命中则直接返回(8.0 后移除) 3. 解析器:词法/语法分析,生成语法树 4. 优化器:选择索引,生成执行计划 5. 执行器:调用存储引擎接口 6. 存储引擎(InnoDB):读写数据(缓冲池、磁盘交互) 7. 返回结果。 | 后端,MySQL,数据库 |
25 | Kafka 中 Zookeeper 的作用? | 在旧版本(KRaft 前)中负责: 1. Broker 注册:维护节点列表与状态 2. Topic 配置:存储分区、副本分配信息 3. 控制器选举:选主 Broker 管理分区leader 4. 消费者组:保存 offset 和组成员(新版 offset 存内部 Topic)。 | Kafka,消息队列,后端 |
26 | MySQL 是如何实现事务的? | 通过 InnoDB 引擎实现: 1. 原子性(A):Undo Log(回滚日志)记录修改前数据 2. 一致性(C):由 A+I+D 共同保证 3. 隔离性(I):锁 + MVCC(多版本并发控制) 4. 持久性(D):Redo Log(重做日志)保证崩溃恢复。 | 后端,MySQL,数据库 |
27 | 为什么 Java 8 移除了永久代(PermGen)并引入了元空间(Metaspace)? | 1. 永久代大小难调优:易触发 OOM 2. 类元数据生命周期与类加载器绑定,回收复杂 3. 元空间使用本地内存(非 JVM 堆),默认无上限(受物理内存限制) 4. 简化 HotSpot 代码,合并 JRockit 特性。元空间 GC 由类加载器触发。 | JVM,Java |
28 | 说一下 Kafka 中关于事务消息的实现? | 保证跨分区原子写入: 1. 事务协调器:每个 Producer 对应一个,管理事务状态 2. 事务ID:标识跨会话事务 3. 两阶段提交: - 发送消息到事务 Topic(未提交) - Commit:写事务结束标记到 __transaction_state 4. 消费者设置 isolation.level=read_committed 过滤未提交消息。 | Kafka,消息队列,后端 |
29 | MySQL 事务的二阶段提交是什么? | 用于保证 binlog 和 redo log 一致性: 1. Prepare 阶段:InnoDB 写 redo log(prepare 状态) 2. Commit 阶段: a. 写 binlog b. InnoDB 写 redo log(commit 状态) 崩溃恢复时:若 binlog 完整则提交事务,否则回滚。 | 后端,MySQL,数据库 |
30 | 说一下 RocketMQ 中关于事务消息的实现? | 半消息机制: 1. 生产者发送半消息(对消费者不可见) 2. Broker 持久化半消息,返回 ACK 3. 生产者执行本地事务 4. 根据本地事务结果提交/回滚: - 提交:消息可见,投递给消费者 - 回滚:删除消息 5. Broker 定时回查:若生产者未响应,回查事务状态。 | RocketMQ,后端,消息队列 |
31 | MySQL 中长事务可能会导致哪些问题? | 1. 锁竞争:持有锁时间长,阻塞其他事务 2. 回滚段膨胀:Undo Log 无法及时清理 3. 数据一致性风险:未提交修改对其他事务可见(取决于隔离级别) 4. 主从延迟:Binlog 需等待事务提交 5. 内存消耗:事务相关缓冲区无法释放。 | 后端,MySQL,数据库 |
32 | RocketMQ 的事务消息有什么缺点?你还了解过别的事务消息实现吗? | 缺点: 1. 消息可见延迟(需等待本地事务结果) 2. 回查机制可能重复执行本地事务 3. 不保证全局事务一致性(需结合业务补偿) 其他实现: - Kafka 事务:基于生产者幂等和事务协调器 - 最大努力通知:通过异步重试保证最终一致。 | 消息队列,后端,RocketMQ |
33 | MySQL 中的 MVCC 是什么? | 多版本并发控制:通过数据快照实现非锁定读。InnoDB 实现方式: 1. 每行数据隐含 DB_TRX_ID(事务ID)和 DB_ROLL_PTR(回滚指针) 2. ReadView 结构:记录活跃事务ID列表,用于判断数据版本可见性 3. 快照读(如 SELECT)基于 ReadView 访问 Undo Log 中的历史版本。 | 后端,MySQL,数据库 |
34 | 为什么需要消息队列? | 核心价值: 1. 解耦:生产者和消费者独立演进 2. 异步:非必要操作异步执行,提升响应速度 3. 削峰:缓冲突发流量,保护下游系统 4. 可靠:消息持久化,确保不丢失 5. 扩展:通过分区水平扩展消费者。 | 消息队列,后端 |
35 | MySQL 中的事务隔离级别有哪些? | 1. 读未提交(Read Uncommitted):可能脏读 2. 读已提交(Read Committed):避免脏读,可能不可重复读 3. 可重复读(Repeatable Read):避免脏读、不可重复读,可能幻读(InnoDB 通过 MVCC 部分避免) 4. 串行化(Serializable):强制事务串行执行。 | 后端,MySQL,数据库 |
36 | 说一下消息队列的模型有哪些? | 1. 点对点(Queue):消息被一个消费者消费 2. 发布/订阅(Topic):消息广播给所有订阅者 3. 增强模型: - Kafka 分区模型:同一分区内顺序消费 - RabbitMQ Exchange:Direct/Fanout/Topic/Headers 路由规则。 | 消息队列,后端 |
37 | MySQL 默认的事务隔离级别是什么?为什么选择这个级别? | 默认级别:可重复读(Repeatable Read)。原因: 1. 平衡性能与一致性:避免脏读和不可重复读 2. InnoDB 通过 MVCC 和 Next-Key Lock 减少幻读 3. 适合多数业务场景(如账户余额查询需结果稳定)。 | 后端,MySQL,数据库 |
38 | 谈谈你了解的最常见的几种设计模式,说说他们的应用场景 | 1. 单例模式(全局唯一实例):配置管理、线程池 2. 工厂模式(解耦创建逻辑):Spring BeanFactory 3. 代理模式(控制访问):AOP 切面、RPC 动态代理 4. 观察者模式(事件通知):GUI 事件、消息队列 5. 策略模式(算法切换):支付方式选择、排序算法切换。 | 设计模式 |
39 | MySQL 中有哪些锁类型? | 按粒度: 1. 表锁:MyISAM 默认,开销小但并发低 2. 行锁:InnoDB 默认,分记录锁(锁定单行)、间隙锁(锁定范围)、临键锁(记录+间隙) 按行为:共享锁(S 锁,读锁)、排他锁(X 锁,写锁)、意向锁(IS/IX)。 | 后端,MySQL,数据库 |
40 | 什么是策略模式?一般用在什么场景? | 定义:封装可互换的算法族,使它们独立于客户端变化。场景: 1. 支付方式选择(支付宝/微信/银行卡) 2. 排序算法切换(快速/归并/堆排序) 3. 折扣策略(满减/折扣率/立减)。 | 设计模式 |
41 | MySQL 的乐观锁和悲观锁是什么? | 悲观锁:假定冲突高,先加锁再操作(SELECT … FOR UPDATE) 乐观锁:假定冲突低,通过版本号/时间戳校验(UPDATE … SET version=new_version WHERE version=old_version),失败重试。 | 后端,MySQL,数据库 |
42 | 什么是责任链模式?一般用在什么场景? | 定义:多个处理器依次处理请求,每个处理器决定是否传递给下一个。场景: 1. 过滤器链(Servlet Filter) 2. 审批流程(经理→总监→CEO) 3. 异常处理(逐级捕获)。 | 设计模式 |
43 | MySQL 中如果发生死锁应该如何解决? | 1. 设置 innodb_deadlock_detect=on(自动检测,默认开启) 2. InnoDB 自动回滚代价较小 |