Java 岗,面试常问 100 题(精简版)

作者:洛水石

适用人群:3 年经验 Java 开发,面中级/高级岗位。本题库按模块分类,每题给出标准回答要点,可在面试前 3 天集中背诵。


一、Java 基础(15题)

**Q1:== 和 equals 的区别?**

  • `==`:比较基本类型时比较值,比较引用类型时比较内存地址

  • `equals()`:默认 Object 实现比较地址,子类(String、Integer)通常重写为比较内容

  • 常见坑:String pool 常量池导致 `new String("abc") == "abc"` 为 false

```java

String s1 = new String("hello");

String s2 = "hello";

System.out.println(s1 == s2); // false(new 的是新对象)

System.out.println(s1.equals(s2)); // true(内容相同)

```


**Q2:final、finally、finalize 的区别?**

  • `final`:修饰类(不可继承)、方法(不可重写)、变量(不可重新赋值)

  • `finally`:`try-catch-finally` 块中的代码,无论是否异常都执行(除非 System.exit())

  • `finalize()`:Object 的方法,GC 回收对象前调用,已废弃(不推荐使用)


**Q3:String、StringBuilder、StringBuffer 的区别?**

| 类 | 线程安全 | 性能 | 使用场景 |

|---|---|---|---|

| String | 安全 | 差(每次new) | 常量字符串 |

| StringBuilder | 不安全 | 好 | 单线程字符串拼接 |

| StringBuffer | 安全(synchronized) | 中 | 多线程字符串拼接 |


**Q4:重载(Overload)和重写(Override)的区别?**

  • **重载(Overload)**:同一个类中,方法名相同但参数列表不同(个数、类型、顺序),编译时多态

  • **重写(Override)**:子类重写父类方法,方法签名完全相同,运行时常多态


**Q5:接口和抽象类的区别?**

| 维度 | 接口 | 抽象类 |

|---|---|---|

| 关键字 | `interface` | `abstract class` |

| 方法 | JDK8前全抽象;JDK8+可default/static | 可抽象可普通 |

| 属性 | 只能是 public static final | 无限制 |

| 多继承 | 支持多接口 | 只支持单继承 |

| 构造器 | 无 | 有 |

面试加分:**JDK9 接口 private 方法**、**函数式接口 @FunctionalInterface**。


**Q6:ArrayList 和 LinkedList 的区别?**

| 维度 | ArrayList | LinkedList |

|---|---|---|

| 底层结构 | 动态数组 | 双向链表 |

| 随机访问 | O(1) | O(n) |

| 插入/删除 | O(n)(搬移元素) | O(1)(指针操作) |

| 内存占用 | 连续,浪费扩容空间 | 每个节点存前后指针 |

选择建议:频繁随机访问 → ArrayList;频繁中间插入删除 → LinkedList。


**Q7:HashMap 的底层实现原理?**

  • JDK7:`数组 + 链表`(Entry数组),哈希冲突用链表解决

  • JDK8:`数组 + 链表 + 红黑树`(链表长度 > 8 且容量 > 64 时转为红黑树)

  • 扰动函数:`(h = key.hashCode()) ^ (h >>> 16)`,减少哈希碰撞

  • 扩容因子:默认 0.75,扩容翻倍重新哈希

```java

// JDK8 红黑树转链表阈值

if (binCount >= TREEIFY_THRESHOLD - 1) // = 7,转为红黑树

```


**Q8:HashMap 是线程安全的吗?有什么替代方案?**

  • HashMap 线程不安全,并发下可能死循环(JDK7 扩容时)

  • 替代方案:

  • `Hashtable`:全局 synchronized,锁粒度大,不推荐

  • `Collections.synchronizedMap()`:同 Hashtable

  • `ConcurrentHashMap`:**推荐**,JDK7分段锁,JDK8+CAS+synchronized


**Q9:ConcurrentHashMap JDK7 和 JDK8 的区别?**

  • **JDK7**:Segment数组 + HashEntry 数组,锁住一个 Segment,其他 Segment 可并发

  • **JDK8**:取消 Segment,改为 `Node数组 + CAS + synchronized`,锁粒度细化到每个桶


**Q10:什么是 CAS?有什么问题?**

  • CAS(Compare-And-Swap):无锁并发,CPU 提供原子指令

  • 三个操作数:内存位置V、预期值A、新值B;V==A 时才更新为B

  • 问题:

  1. **ABA问题**:加版本号(AtomicStampedReference)解决

  2. 自旋开销:竞争激烈时大量空转

  3. 只能保证一个共享变量原子性


**Q11:synchronized 和 ReentrantLock 的区别?**

| 维度 | synchronized | ReentrantLock |

|---|---|---|

| 锁类型 | 悲观锁/可重入 | 可重入 |

| 等待可中断 | 否 | 是(tryLock) |

| 公平锁 | 否(默认非公平) | 可选(fair=true) |

| 条件变量 | 内置(wait/notify) | 多个(newCondition) |

| 释放方式 | 自动释放 | 必须 finally unlock() |


**Q12:ThreadLocal 有什么用?有什么坑?**

  • **作用**:为每个线程提供独立的变量副本,实现线程隔离

  • **典型场景**:SimpleDateFormat(线程不安全)、数据库连接、Session管理

  • **坑**:

  • 内存泄漏:`ThreadLocalMap` Entry 弱引用 `WeakReference<ThreadLocal>`,但Value是强引用。正确做法:用完 `remove()`。

  • 父子线程:InheritableThreadLocal 可继承,但线程池场景下不行(用 Alibaba TransmittableThreadLocal)


**Q13:volatile 关键字的作用?**

  • **可见性**:每次读取强制从主内存读,每次写入强制刷新到主内存

  • **有序性**:禁止指令重排序(内存屏障)

  • **不保证原子性**:`volatile int count++;` 仍不是线程安全的(需要 AtomicInteger)


**Q14:sleep() 和 wait() 的区别?**

| 维度 | sleep() | wait() |

|---|---|---|

| 所属 | Thread 静态方法 | Object 实例方法 |

| 锁释放 | 不释放 | 释放锁 |

| 唤醒方式 | 超时自动醒 | notify()/notifyAll() 或超时 |

| 使用场景 | 模拟延迟、轮询 | 生产者-消费者等待 |


**Q15:线程池的七大参数?拒绝策略?**

```java

public ThreadPoolExecutor(

int corePoolSize, // 核心线程数

int maximumPoolSize, // 最大线程数

long keepAliveTime, // 空闲线程存活时间

TimeUnit unit, // 时间单位

BlockingQueue<Runnable> workQueue, // 任务队列

ThreadFactory threadFactory, // 线程工厂

RejectedExecutionHandler handler // 拒绝策略

)

```

拒绝策略:

  • `AbortPolicy`(默认):抛RejectedExecutionException

  • `CallerRunsPolicy`:调用者线程执行

  • `DiscardPolicy`:丢弃任务

  • `DiscardOldestPolicy`:丢弃队列最老的任务


二、Java 并发(10题)

**Q16:线程的创建方式?哪种最好?**

  1. 继承 `Thread`(单继承局限)

  2. 实现 `Runnable`(无返回值)

  3. 实现 `Callable + FutureTask`(有返回值,可抛异常)

  4. **线程池**(最佳,资源复用、线程管理)

生产环境严禁 `new Thread()`,统一使用线程池。


**Q17:线程的生命周期?**

```

NEW → RUNNABLE → BLOCKED → WAITING → TIMED_WAITING → TERMINATED

```

  • NEW:创建未启动

  • RUNNABLE:就绪(Ready)或运行中(Running)

  • BLOCKED:等待锁(synchronized)

  • WAITING:wait()/join()/LockSupport.park()

  • TIMED_WAITING:sleep(n)/wait(n)/await(n,TimeUnit)


**Q18:Synchronized 锁升级过程?**

无锁 → 偏向锁 → 轻量级锁 → 重量级锁(不可降级)

  • **偏向锁**:Mark Word 记录线程ID,同一线程进入轻量级锁CAS替换Mark Word

  • **轻量级锁**:自旋 CAS 抢锁,自旋失败一定程度后膨胀

  • **重量级锁**:ObjectMonitor,线程阻塞,用户态→内核态切换,开销大

**锁粗化、锁消除**是JIT编译器的优化手段。


**Q19:什么是死锁?四个必要条件?如何排查?**

四个必要条件:互斥、占有并等待、不可抢占、循环等待

**排查命令**:

```bash

jstack <pid> # 查找死锁线程

或 VisualVM / Arthas

```

**避免死锁**:统一加锁顺序、使用锁超时、使用并发工具(Phaser、CyclicBarrier)


**Q20:JUC 常用并发工具?**

| 类 | 作用 |

|---|---|

| `CountDownLatch` | 倒计时闩,某线程等待N个操作完成 |

| `CyclicBarrier` | 循环栅栏,N个线程相互等待到齐后一起执行 |

| `Semaphore` | 信号量,控制并发访问资源数量 |

| `Exchanger` | 两个线程交换数据 |


三、JVM(10题)

**Q21:JVM 内存区域划分?**

  • **线程私有**:程序计数器(无OOM)、虚拟机栈(StackOverflowError)、本地方法栈

  • **线程共享**:堆(OOM高发区)、方法区/元空间(OOM)

JDK8 将方法区改为元空间(Metaspace),使用直接内存,不在堆中。


**Q22:对象的创建过程?**

  1. 类加载检查(常量池)

  2. 分配内存(指针碰撞 / 空闲列表)

  3. 初始化零值

  4. 设置对象头(Mark Word、类型指针)

  5. 执行构造器

并发分配:CAS + 失败重试 / TLAB(线程本地分配缓存)


**Q23:Minor GC 和 Full GC 的触发条件?**

  • **Minor GC**:Eden区满,Survivor区交换

  • **Full GC**:

  • 老年代空间不足

  • System.gc()(建议触发,不一定)

  • Metaspace 不足

  • Minor GC 前的安全检查(-XX:PretenureSizeThreshold)


**Q24:CMS 和 G1 的区别?**

| 维度 | CMS | G1 |

|---|---|---|

| 收集范围 | 老年代 | 全堆(老年代+年轻代) |

| 回收方式 | 标记-清除(内存碎片) | 标记-整理(无碎片) |

| 停顿时间 | STW时间长 | 可控制(-XX:MaxGCPauseMillis) |

| 分代方式 | 物理分代 | 逻辑分代(Region) |

**G1 是 JDK9+ 默认收集器**,适合大堆(>6GB)低延迟场景。


**Q25:什么对象会进入老年代?**

  1. 大对象(`-XX:PretenureSizeThreshold`,直接进老年代)

  2. 长期存活对象(Survivor 中年龄 >= `-XX:MaxTenuringThreshold`,默认15)

  3. 动态年龄判断:Survivor 中相同年龄所有对象总和 > Survivor 空间一半,则 >= 该年龄的直接进老年代


**Q26:类加载过程?双亲委派模型?**

加载 → 验证 → 准备 → 解析 → 初始化

**双亲委派**:类加载请求委派给父类加载器处理,最终顶层的 BootstrapClassLoader。若父类找不到,才自己加载。

好处:类唯一性保证、安全性(防止核心API被篡改,如自定义String)


**Q27:什么情况下会触发类初始化?**

  1. new、读取/设置静态字段(final已在编译期放入常量池的除外)

  2. 调用静态方法

  3. 反射 `Class.forName()`

  4. 初始化子类时先初始化父类

  5. 主类(main方法所在类)先初始化


**Q28:常用的 JVM 调优参数?**

```bash

-Xms512m -Xmx512m # 堆初始/最大

-Xss1m # 线程栈大小

-XX:MetaspaceSize=256m # 元空间

-XX:+UseG1GC # 使用G1收集器

-XX:MaxGCPauseMillis=200 # 最大GC停顿时间

-XX:+HeapDumpOnOutOfMemoryError # OOM时导出堆dump

-XX:HeapDumpPath=/tmp/heap.hprof

```


**Q29:如何定位 OOM 问题?**

  1. 添加参数 `-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=`

  2. MAT(Memory Analyzer Tool)分析堆 dump

  3. ** Arthas dashboard** 查看内存使用

  4. 查看 GC 日志:`GCLogFileSize`、`PrintGCDetails`


**Q30:JVM 三色标记算法?**

  • 白色:未标记,垃圾

  • 灰色:自身已标记,但引用的对象未标记

  • 黑色:自身和引用对象都已标记

**并发问题**:漏标(漏标对象被当作垃圾)和多标(不该回收的对象被回收)。解决方案:**SATB(Snapshot-At-The-Beginning,G1使用)** 和 **增量更新(CMS使用)**。


四、MySQL(10题)

**Q31:MySQL 存储引擎?InnoDB 和 MyISAM 的区别?**

| 维度 | InnoDB | MyISAM |

|---|---|---|

| 事务 | 支持 | 不支持 |

| 外键 | 支持 | 不支持 |

| 锁粒度 | 行锁(高并发) | 表锁 |

| 全文索引 | 5.6+ 支持 | 支持 |

| 崩溃恢复 | 自动恢复(redo log) | 差 |

| 主键 | 必须有主键,无则自动生成 | 可无 |


**Q32:索引失效的常见场景?**

  1. `WHERE id + 1 = 10`(计算)

  2. `WHERE name LIKE '%abc'`(前导%)

  3. `WHERE age > 18 ORDER BY name`(违反最左前缀)

  4. `WHERE status = 1 AND id > 5`(范围之后全失效)

  5. `WHERE IS NULL`(允许NULL但可能失效)

  6. `WHERE LENGTH(name) > 5`(函数)


**Q33:什么是回表?如何避免?**

回表:先查普通索引拿到主键ID,再回主键索引查完整行数据(2次B+树)。

避免方法:

  • 覆盖索引:查询字段全部在索引中,无需回表

  • `SELECT *` 容易回表,尽量 `SELECT 具体字段`

```sql

-- 覆盖索引示例(无需回表)

SELECT name, age FROM user WHERE name = 'Tom';

-- 索引:(name, age) 即可覆盖

```


**Q34:事务隔离级别?各有什么问题?**

| 级别 | 脏读 | 不可重复读 | 幻读 |

|---|---|---|---|

| READ UNCOMMITTED | 可能 | 可能 | 可能 |

| READ COMMITTED | 不可能 | 可能 | 可能 |

| REPEATABLE READ(MySQL默认) | 不可能 | 不可能 | 可能 |

| SERIALIZABLE | 不可能 | 不可能 | 不可能 |

InnoDB 在 RR 级别通过 MVCC + Next-Key Lock 基本解决幻读问题。


**Q35:MVCC 原理?**

  • 隐藏列:`trx_id`(事务版本号)、`roll_pointer`(指向undo log链)

  • ReadView:快照读时生成,包含 `m_ids`(活跃事务ID列表)、`min_trx_id`、`max_trx_id`

  • 可见性判断:`trx_id < min_trx_id` → 可见;`trx_id > max_trx_id` → 不可见(其他事务提交);中间则检查是否在活跃列表中


**Q36:一条 SQL 执行很慢,如何排查?**

  1. `EXPLAIN` 查看执行计划(type、key、rows、Extra)

  2. 检查是否全表扫描(type=ALL)

  3. 检查索引是否失效

  4. `SHOW STATUS LIKE 'Slow_queries'` 查看慢查询

  5. 开启慢查询日志(`slow_query_log`)

  6. `SHOW OPEN TABLES` 检查锁等待


**Q37:什么是主从复制?原理?**

```

Master → Binlog → Dump线程 → Relay Log → SQL线程 → Slave

```

  • Master:记录数据变更到 Binlog

  • Slave:IO线程读取Binlog写入Relay Log,SQL线程重放Relay Log

  • 同步方式:异步复制 / 半同步复制(5.7+)


**Q38:分库分表后,如何保证 ID 全局唯一?**

  1. UUID(无序、存储大)

  2. 数据库自增(单库没问题,跨库需独立ID服务)

  3. **雪花算法(Snowflake)**:41位时间戳+10位机器ID+12位序列号,趋势递增

  4. 百度 UidGenerator / 美团 Leaf


**Q39:InnoDB 锁有哪些类型?**

  • 行锁:共享锁(S)、排他锁(X)

  • 意向锁:表级,IS/IX(防止加表锁时逐行检查)

  • Gap锁:间隙锁,锁定范围(左开右开区间)

  • Next-Key锁:记录锁+间隙锁,左开右闭(解决幻读)

  • 插入意向锁(Insert Intention Lock)


**Q40:一条 UPDATE 语句的执行过程?**

  1. 解析器解析 SQL

  2. 优化器选择索引

  3. 执行器调用存储引擎接口

  4. 写入 redo log(Prepare)→ 写入 undo log

  5. 修改 Buffer Pool 中的数据页

  6. 事务提交时写入 binlog

  7. redo log 标记为 Commit

**两阶段提交**:保证 redo log 和 binlog 一致性。


五、Spring(10题)

**Q41:Spring 循环依赖如何解决?**

三级缓存:

  • **singletonObjects**(一级):完全初始化好的单例Bean

  • **earlySingletonObjects**(二级):提前暴露,未完全初始化的Bean

  • **singletonFactories**(三级):ObjectFactory,提前暴露的Bean工厂

```

A创建 → 提前暴露A工厂 → A发现依赖B → B创建 → 提前暴露B工厂

→ B发现依赖A → 从三级缓存拿到A的ObjectFactory → 创建代理 → 放入二级缓存

```

**构造器注入导致的循环依赖无法解决**,必须改用 Setter 注入或 @Lazy。


**Q42:@Autowired 和 @Resource 的区别?**

| 维度 | @Autowired | @Resource |

|---|---|---|

| 来源 | Spring | JDK(JSR-250) |

| 注入方式 | byType → byName | byName → byType |

| required属性 | 支持(默认true) | 不支持 |

| 位置 | 字段/构造器/setter | 字段/setter |


**Q43:BeanFactory 和 FactoryBean 的区别?**

  • **BeanFactory**:Spring IOC容器最底层接口,负责Bean的创建和依赖注入

  • **FactoryBean**:工厂Bean,用于创建复杂Bean(如MyBatis SqlSessionFactory)

MyBatis `MapperFactoryBean` 实现了 FactoryBean,这就是为什么 `@MapperScan` 后可以直接注入 Mapper。


**Q44:Spring 事务失效的场景?**

  1. 方法非public(代理限制)

  2. 自调用(同类内部方法调用,不经过代理)

  3. 异常被 catch 吞掉了(需 `TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()`)

  4. 事务传播行为不当(PROPAGATION_REQUIRES_NEW)

  5. 非Spring管理的Bean(new出来的对象)

  6. 数据库不支持事务(MySQL MyISAM)


**Q45:Spring MVC 请求处理流程?**

```

请求 → DispatcherServlet → HandlerMapping → HandlerExecutionChain

→ HandlerAdapter → Handler(Controller)

→ ViewResolver 解析视图名 → View 渲染响应

```


**Q46:Spring Boot 自动装配原理?**

  1. `@SpringBootApplication` = `@Configuration` + `@EnableAutoConfiguration` + `@ComponentScan`

  2. `META-INF/spring.factories` 或 `META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports`

  3. `EnableAutoConfiguration` 导入所有 `AutoConfigurationImportSelector`

  4. `@Conditional` 系列注解按需实例化Bean


**Q47:Spring Bean 的生命周期?**

```

实例化 → 属性填充(@Autowired) → BeanNameAware → BeanFactoryAware

→ ApplicationContextAware → BeanPostProcessor.postProcessBeforeInitialization

→ @PostConstruct → InitializingBean.afterPropertiesSet → 自定义init-method

→ BeanPostProcessor.postProcessAfterInitialization → 使用中

→ @PreDestroy → DisposableBean.destroy → 自定义destroy-method

```


**Q48:Spring 用了哪些设计模式?**

| 模式 | 应用 |

|---|---|

| 单例模式 | Bean默认单例 |

| 工厂模式 | BeanFactory |

| 代理模式 | AOP(@AspectJ) |

| 策略模式 | Resource实现 |

| 模板方法 | JdbcTemplate、HibernateTemplate |

| 观察者模式 | ApplicationEvent、ApplicationListener |


**Q49:MyBatis #{} 和 ${} 的区别?**

  • `#{}`:**预编译参数**,防止SQL注入,用 `?` 占位

  • `${}`:**直接拼接**,有SQL注入风险,仅适用于表名/列名动态传入

```java

// #{} 安全

SELECT * FROM user WHERE id = #{id} → SELECT * FROM user WHERE id = ?

// ${} 有注入风险

SELECT * FROM ${tableName} → 直接拼接

```


**Q50:MyBatis 一级缓存和二级缓存?**

  • **一级缓存**(SqlSession级别):同一个 SqlSession 中,查询结果缓存(Map),事务提交/关闭时清空

  • **二级缓存**(Mapper级别):跨 SqlSession,需手动开启;`cacheEnabled=true`;注意:实体类需实现 Serializable

  • 缓存失效:任何增删改操作(`flushCache=true`)


六、微服务与分布式(10题)

**Q51:Dubbo 和 Spring Cloud 的区别?**

| 维度 | Dubbo | Spring Cloud |

|---|---|---|

| 通信协议 | RPC(Dubbo协议/Netty) | HTTP(RESTful) |

| 性能 | 高(二进制序列化) | 较低(JSON序列化) |

| 功能完整性 | 缺配置/网关等 | 全家桶,生态完整 |

| 适用场景 | 高并发内部微服务 | 异构系统、开放API |

**选型建议**:内部高性能通信选Dubbo;需要快速集成、异构系统选Spring Cloud Alibaba。


**Q52:Nacos 的注册与发现原理?**

  1. 服务启动时向 Nacos 注册自身(IP、Port、实例ID)

  2. Nacos 心跳检测(默认5秒),超时15秒标记为不健康,30秒删除

  3. 消费者从 Nacos 获取服务列表,本地缓存 + 定时更新

  4. Nacos 支持临时实例(心跳)和永久实例(不主动删除)


**Q53:Sentinel 的三大功能?**

  1. **流量控制**:QPS/并发线程数限流,滑动窗口算法

  2. **熔断降级**:RT阈值/异常比例触发熔断,Sentinel 采用半开状态恢复

  3. **系统自适应保护**:根据系统 Load、CPU、入口QPS自适应限流


**Q54:分布式 ID 方案对比?**

| 方案 | 优点 | 缺点 |

|---|---|---|

| UUID | 无中心 | 无序、存储大 |

| 数据库自增 | 简单有序 | 单库瓶颈 |

| 雪花算法 | 高效、有序 | 依赖时钟 |

| Redis INCR | 高性能 | 依赖Redis |


**Q55:幂等性如何保证?**

  1. **数据库唯一索引**:重复插入会报错

  2. **状态机**:订单状态只能正向流转(待支付→已支付)

  3. **Token 机制**:Redis 存Token,只用一次

  4. **乐观锁/悲观锁**:版本号控制


**Q56:接口超时怎么处理?**

  1. **重试**:接口幂等,重试间隔递增(指数退避)

  2. **熔断**:Sentinel/Hystrix,超时比例高时熔断

  3. **降级**:返回兜底数据或缓存旧数据

  4. **超时设置**:读服务 200-500ms,写服务 100-200ms


**Q57:如何保证接口的幂等性?**

  • Token 机制:前端获取Token → 提交时携带 → 后端 Redis 扣减(SETNX)

  • 唯一键:数据库唯一索引(如 orderId + actionType)

  • 悲观锁/乐观锁:`SELECT ... FOR UPDATE` 或 `version` 字段

  • 消息队列:消费端去重


**Q58:Feign 的超时配置?**

```yaml

feign:

client:

config:

default:

connectTimeout: 2000 # 连接超时

readTimeout: 5000 # 读取超时

```

Ribbon 的默认超时是 1 秒,易导致超时,需要合理配置。


**Q59:Sleuth + Zipkin 链路追踪原理?**

  1. 每个请求生成 `TraceID`(全局唯一)

  2. 每经过一个服务,在 HTTP Header 中传递 `TraceID` 和 `SpanID`

  3. 采样率:`-Dspring.sleuth.sampler.probability=0.1`

  4. Zipkin 收集器汇总数据,提供可视化 UI


**Q60:Seata AT 模式的原理?**

  1. 解析业务SQL,记录**前镜像**(修改前数据快照)

  2. 执行 SQL,记录**后镜像**(修改后数据快照)

  3. 事务成功 → 前后镜像无操作,提交;失败 → 用前镜像反向更新(undo log)

  4. 全局锁保证事务隔离


七、Redis(10题)

**Q61:Redis 数据类型及底层实现?**

| 类型 | 底层实现 | 常用场景 |

|---|---|---|

| String | SDS(动态字符串) | 缓存、计数器、分布式锁 |

| List | quicklist(压缩链表) | 消息队列、任务队列 |

| Hash | dict(哈希表)+ ziplist | 对象存储 |

| Set | intset/dict | 标签、好友关系 |

| ZSet | 压缩列表/跳表 | 排行榜、延时队列 |

| Bitmap | 位图 | 签到、布隆过滤器前置 |

| HyperLogLog | PFADD/PFCOUNT | UV统计 |


**Q62:Redis 过期 key 的删除策略?**

  • **惰性删除**:访问时检查过期,过期才删(内存浪费但不占用CPU)

  • **定期删除**:每隔100ms随机抽查,过期则删除(平衡方案)

  • **定时删除**(主动):设置expire时同时创建定时器,TTL到期立即删除(占用CPU)

Redis 实际采用:**惰性删除 + 定期删除**组合。


**Q63:Redis 持久化机制?RDB 和 AOF 的区别?**

| 维度 | RDB | AOF |

|---|---|---|

| 方式 | 定时快照,生成dump.rdb | 记录每个写命令 |

| 文件大小 | 小(二进制) | 大(文本) |

| 恢复速度 | 快 | 慢 |

| 数据完整性 | 可能丢失最近数据 | 取决于刷盘策略(always/everysec/no) |

| 资源消耗 | fork子进程BGSAVE | 主线程追加文件 |

**建议**:同时开启 RDB + AOF,优先用 AOF 恢复。


**Q64:Redis 内存淘汰策略?**

| 策略 | 说明 |

|---|---|

| noeviction | 不淘汰,返回错误(默认) |

| allkeys-lru | 所有Key,LRU |

| allkeys-random | 所有Key,随机 |

| volatile-lru | 有过期Key,LRU |

| volatile-random | 有过期Key,随机 |

| volatile-ttl | 有过期Key,TTL最短优先 |

| allkeys-lfu | 所有Key,LFU(Redis4.0+) |


**Q65:Redis 主从复制原理?**

```

Master → SYNC/PSYNC → RDB + 缓冲区 → 全量同步

Master → 命令传播 → 增量同步(Repl Backlog Buffer)

```

  • 旧版:SYNC(全量),开销大

  • 新版:PSYNC(部分重同步),通过 `repl_backlog_buffer` 支持增量


**Q66:Redis 集群方案?**

  1. **主从复制**:一主多从,读写分离(从节点最终一致)

  2. **哨兵(Sentinel)**:自动故障转移,监控/选主/通知(3节点以上)

  3. **Codis/Redis Cluster**:数据分片(16384槽),每个分片一主多从


**Q67:Redis Cluster 的数据分片原理?**

  • 16384 个槽(slot)

  • 每个节点负责一部分槽:`slot = CRC16(key) % 16384`

  • MOVED 重定向:客户端请求到错误节点,节点返回正确节点地址

  • ASK 重定向:集群迁移中使用(临时)


**Q68:Redis 分布式锁?Redisson 原理?**

基础版(不推荐):`SETNX + EXPIRE`(非原子性,可能死锁)

**正确方案**:`SET key value NX PX 30000`(原子性)

Redisson 原理:

  • 看门狗(Watchdog):自动续期,每10秒检查

  • 可重入锁:`hash {uuid:threadId -> count}`

  • RedissonLock 底层:Lua 脚本保证原子性


**Q69:Redis 和 MySQL 如何保持一致性?**

| 模式 | 说明 | 一致性 |

|---|---|---|

| Cache Aside(旁路缓存) | 读:Cache Miss → DB → 写Cache;写:DB → 删除Cache | 最终一致 |

| Read Through | 读:Cache自动从DB加载 | 最终一致 |

| Write Through | 写:先写Cache再写DB | 强一致 |

| Write Behind | 写:先写Cache,异步批量写DB | 不一致 |

**推荐**:Cache Aside(旁路缓存),先删Cache再更新DB(双删解决并发问题)。


**Q70:Redis 大 Key 问题?**

  • **发现**:`redis-cli --bigkeys`、`SCAN + STRLEN`

  • **影响**:阻塞单线程、内存不均匀、集群倾斜

  • **解决**:拆分为多个小Key(hash slot分散)、压缩(ziplist)、删除时用 UNLINK(异步)


八、框架与工具(10题)

**Q71:Kafka 为什么这么快?**

  1. **顺序写入**:追加写磁盘,顺序IO速度接近内存

  2. **页缓存(Page Cache)**:利用OS缓存,热点数据在内存

  3. **零拷贝**:sendfile + DMA,直接内核空间传输,无需应用层Copy

  4. **批量处理**:批量发送、批量压缩(降低网络开销)

  5. **分区并行**:Producer/Consumer以Partition为单位并行


**Q72:RabbitMQ 消息顺序性如何保证?**

  • **问题来源**:多消费者并发消费

  • **解决方案**:

  1. 单分区(牺牲性能)

  2. 消息加序列号,消费端按序列号重排序

  3. 同一个业务Key路由到同一队列(使用 routingKey 哈希)


**Q73:ES 查询流程?**

```

Query Phase:协调节点 → 所有分片并行搜索 → 收集TopN → 排序

Fetch Phase:协调节点 → 各分片获取完整文档 → 合并返回客户端

```

  • 分片副本:可配置 `replication=sync/async`

  • 深度分页:`search_after` 替代 `from+size`


**Q74:Docker 常用命令?**

```bash

docker build -t myapp:1.0 . # 构建镜像

docker run -d -p 8080:8080 myapp # 运行容器

docker ps -a # 查看所有容器

docker exec -it <id> /bin/bash # 进入容器

docker logs -f <id> # 查看日志

docker-compose up -d # 启动编排

```


**Q75:Git 常用命令?**

```bash

git checkout -b feature/login # 创建并切换分支

git stash / git stash pop # 暂存工作区

git rebase -i HEAD~3 # 合并提交

git cherry-pick <commit> # 摘取单个提交

git reset --soft HEAD~1 # 撤销提交(保留改动)

```


**Q76:Linux 常用排查命令?**

```bash

top / htop # CPU/内存

vmstat 1 # 系统监控

netstat -tunlp | grep port # 端口占用

iostat -x 1 # 磁盘IO

ss -s # Socket统计

curl -v http://api/health # HTTP健康检查

```


**Q77:GitFlow 工作流?**

```

main → hotfix(紧急修复)→ main

↑ ↓

release develop → feature → develop

```

  • **main**:生产环境代码,只接受 hotfix 和 release

  • **develop**:开发主分支

  • **feature**:功能分支,从 develop 拉出

  • **release**:发布分支,测试修复后合并到 main + develop


**Q78:Nginx 反向代理和负载均衡配置?**

```nginx

upstream backend {

server 127.0.0.1:8080 weight=3;

server 127.0.0.1:8081 weight=1;

ip_hash; # IP哈希会话保持

}

server {

listen 80;

location / {

proxy_pass http://backend;

proxy_set_header Host $host;

proxy_set_header X-Real-IP $remote_addr;

}

}

```


**Q79:如何设计一个高并发系统?**

  1. **分层设计**:CDN → Nginx → 网关 → 应用 → 缓存 → 数据库

  2. **缓存**:本地缓存(Caffeine)+ 分布式缓存(Redis)

  3. **异步化**:消息队列(削峰、解耦)

  4. **数据库优化**:读写分离、分库分表、索引优化

  5. **限流熔断**:Sentinel / Redis 令牌桶

  6. **弹性伸缩**:K8s HPA


**Q80:用过哪些设计模式?举例说明?**

| 模式 | 示例 |

|---|---|

| 单例 | ApplicationContext、Runtime |

| 工厂 | BeanFactory、SqlSessionFactory |

| 代理 | Spring AOP、MyBatis Mapper |

| 策略 | Redis 序列化策略、支付方式 |

| 装饰器 | BufferedInputStream 包装 FileInputStream |

| 观察者 | ApplicationEvent、Spring Lifecycle |

| 模板方法 | JdbcTemplate、RestTemplate |

| 建造者 | StringBuilder、Alert.builder() |


九、智力 & 综合(5题)

**Q81:如何设计一个抢红包功能?**

  1. **Redis List** 预存红包金额,LPOP 原子领取

  2. **数据库乐观锁**:版本号控制,防止超卖

  3. **MQ 异步**:异步写订单,避免雪崩

  4. **限流**:单用户 QPS 限制


**Q82:接口压测怎么做?**

  1. JMeter /wrk / ab 压测

  2. 关注指标:QPS、RT(P50/P99)、错误率、CPU/内存

  3. 逐步加压,找到系统瓶颈

  4. `redis-benchmark -q -c 100 -n 10000`


**Q83:如何保证消息不丢失?**

  • **生产者**:confirm 回调确认

  • **MQ**:持久化(durable)+ 集群多副本(Raft)

  • **消费者**:手动 ACK 确认,处理完再 ACK


**Q84:你最近在学什么?**

这个问题必问。回答策略:说一个跟公司业务相关的前沿技术(e.g. WebAssembly、云原生、AI辅助编程),展示学习热情和方向性。


**Q85:你有什么问题想问我的?**

绝对不能不问,也不要问薪酬福利(等HR面)。推荐问:

1. 团队规模和使用的技术栈?

2. 这个岗位最大的挑战是什么?

3. 团队的技术成长路径?


十、面试加分话术(3条)

**当被问到"你还有什么要补充的"时:**

"我想补充一点我对这个岗位的理解。刚才聊到的这些技术栈中,我对 XX 特别感兴趣,也做过一些实践,比如 XX 项目。如果有幸加入,我希望能在这个方向深入。"

**当被问到"你的离职原因"时:**

"前公司业务发展到了稳定期,我个人更希望到一家快速成长的公司,学习大规模高并发系统的实践经验。"

**当被问到"你的优点/缺点"时:**

"优点:我喜欢系统性思考,做需求时会先梳理清楚边界和异常情况再动手,代码质量意识比较强。

缺点:有时候追求完美,可能会在细节上花比较多时间。后来我学会了用番茄工作法来平衡质量和效率。"


本题库覆盖 Java 基础、并发、JVM、MySQL、Spring、Redis、微服务、框架工具8大模块。建议面试前3天,每天过一遍对应模块。完整版100题可在回复「Java100」获取。

>

觉得有用?关注更多硬核技术文章每周更新。

相关推荐
sinat_255487816 小时前
收藏品·学习笔记
java·javascript·windows·学习·microsoft
一碗面4216 小时前
Spring AI 多模态能力全景
java·spring·spring ai
01漫游者6 小时前
JavaScript内存管理与闭包
开发语言·javascript·ecmascript
信徒_6 小时前
服务治理技术选型
java
枕星而眠6 小时前
C语言数组专题:从一维到二维,吃透内存与指针
java·数据结构·算法
Andya_net6 小时前
Spring | 深度剖析Spring Bean的生命周期:从加载到销毁的完整流程
java·spring·rpc
玛卡巴卡ldf6 小时前
【Springboot升级AI】(大模型部署)LangChain4j、会话记忆、隔离消失持久化问题、ollama、RAG知识库、Tools工具
java·开发语言·人工智能·spring boot·后端·springboot
zmzb01036 小时前
C++课后习题训练记录Day120
开发语言·c++
Maiko Star6 小时前
Spring AI 多轮对话记忆(ChatMemory)保姆级教程:从内存版到 Redis 持久化
java·redis·spring