Java面试常见场景题及参考答案
多线程与并发
场景题:如何实现一个线程安全的单例模式?
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
双重检查锁定(DCL)结合volatile
关键字,避免指令重排序和确保可见性。
场景题:HashMap
线程不安全的表现是什么?如何解决? 在多线程环境下,HashMap
的扩容操作可能导致死循环或数据丢失。解决方案:
- 使用
ConcurrentHashMap
- 使用
Collections.synchronizedMap()
- 改用
Hashtable
(性能较低)
JVM与性能优化
场景题:OOM如何排查?
- 使用
jmap -heap <pid>
查看堆内存分配 - 通过
jstat -gcutil <pid>
观察GC情况 - 分析
-XX:+HeapDumpOnOutOfMemoryError
生成的dump文件 - 常见原因:内存泄漏、堆设置过小、大对象分配
场景题:Young GC频繁可能是什么原因?
- Survivor区空间不足
- Eden区设置过小
- 短生命周期对象过多
- 存在内存泄漏导致对象过早晋升
Spring框架
场景题:@Autowired
和@Resource
区别
@Autowired
按类型注入,需配合@Qualifier
指定名称@Resource
默认按名称匹配,支持JSR-250标准@Autowired
属于Spring生态,@Resource
是Java标准注解
场景题:Spring事务失效的常见场景
- 方法非public修饰
- 自调用(同一类中方法调用)
- 异常类型未被捕获(默认只回滚RuntimeException)
- 数据库引擎不支持事务(如MyISAM)
数据库与缓存
场景题:MySQL死锁如何分析?
- 查看
SHOW ENGINE INNODB STATUS
的死锁日志 - 分析事务隔离级别和加锁顺序
- 常见解决方案:调整事务大小、统一访问顺序、使用乐观锁
场景题:Redis缓存穿透解决方案
- 布隆过滤器拦截无效请求
- 缓存空对象(设置短TTL)
- 接口层增加参数校验
- 热点数据预加载
系统设计
场景题:如何设计一个分布式ID生成器?
- UUID:简单但无序且存储空间大
- 数据库自增序列:需要中心化数据库
- Snowflake算法:64位ID(时间戳+机器ID+序列号)
- Redis原子操作:INCR或INCRBY命令
场景题:秒杀系统设计要点
- 分层削峰:前端限流+中间层排队+后端异步处理
- 库存预热:提前缓存库存数据
- 读写分离:查询走缓存,写入用队列
- 熔断降级:核心与非核心服务隔离
代码实践
场景题:反转链表实现
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode nextTemp = curr.next;
curr.next = prev;
prev = curr;
curr = nextTemp;
}
return prev;
}
场景题:手写生产者消费者模型
public class BlockingQueueExample {
private final BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(10);
class Producer implements Runnable {
public void run() {
try {
queue.put(new Random().nextInt());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
class Consumer implements Runnable {
public void run() {
try {
System.out.println(queue.take());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
注意:实际面试中应结合具体业务场景展开,以上答案为通用性解决方案,需根据面试官追问进行深度延伸。
并发与多线程场景
-
场景:如何设计一个线程安全的计数器?
-
核心点: 高并发下的原子性和可见性。
-
方案:
-
synchronized
方法/代码块:简单,但性能较差(锁粒度粗)。 -
ReentrantLock
:更灵活(可中断、公平锁等),性能可能略优于synchronized
(取决于竞争程度)。 -
AtomicInteger
/AtomicLong
:最佳选择(针对计数器)。底层使用 CAS (Compare-And-Swap) 操作,无锁,性能极高。incrementAndGet()
,getAndIncrement()
等方法是原子的。 -
LongAdder
(Java 8+):在超高并发写多读少的场景下性能优于AtomicLong
。采用分段思想分散竞争。
-
-
答题要点: 优先推荐
AtomicInteger
/AtomicLong
,解释 CAS 原理和优势;提到LongAdder
适用场景;说明synchronized
和ReentrantLock
的适用性但非最优。
-
-
场景:设计一个连接池(如数据库连接池)。需要考虑哪些因素?如何实现核心功能(获取连接、释放连接、管理连接数)?
-
核心点: 资源管理、并发控制、连接复用、超时处理、健康检查。
-
方案要素:
-
数据结构: 使用
BlockingQueue
(如LinkedBlockingQueue
或ArrayBlockingQueue
) 存放空闲连接。天然支持并发阻塞操作。 -
初始化: 启动时创建一定数量的连接放入队列。
-
获取连接 (
getConnection()
):-
尝试从队列取连接 (
poll()
非阻塞或take()
阻塞)。 -
如果队列为空且未达最大连接数,创建新连接。
-
如果已达最大连接数,等待 (
take()
) 或超时失败 (poll(timeout)
),或抛出异常。
-
-
释放连接 (
releaseConnection(Connection conn)
):-
检查连接是否有效(可选,或由健康检查处理)。
-
如果有效且空闲队列未满,放回队列 (
offer()
或put()
)。 -
如果无效或队列已满,直接关闭连接。
-
-
管理:
-
最大/最小连接数: 配置参数控制。
-
超时: 获取连接超时 (
poll(timeout)
),连接空闲超时(后台线程扫描队列,关闭超时空闲连接)。 -
健康检查: 定期或使用前验证连接有效性。
-
拒绝策略: 当连接池耗尽且无法创建新连接时如何处理新请求(抛异常、等待、降级)。
-
-
-
答题要点: 强调使用
BlockingQueue
简化并发控制;说明核心方法的流程;提到关键管理特性(超时、健康检查、大小限制);可以对比开源池(如 HikariCP)的设计理念。
-
-
场景:设计一个生产者-消费者模型。生产者不断生产任务放入队列,消费者从队列取出任务处理。如何保证线程安全和高效率?
-
核心点: 线程间安全通信、解耦、流量控制。
-
方案:
-
核心组件:
BlockingQueue
(如LinkedBlockingQueue
,ArrayBlockingQueue
)。 -
生产者 (
Producer
): 调用queue.put(task)
或queue.offer(task, timeout)
放入任务。如果队列满,put
会阻塞,offer
可超时或返回 false。 -
消费者 (
Consumer
): 调用queue.take()
或queue.poll(timeout)
取出任务处理。take
在队列空时阻塞。 -
线程池: 消费者通常使用线程池 (
ExecutorService
) 管理,方便并发处理和资源控制。 -
优雅关闭: 使用
poison pill
(特殊标记任务)通知消费者停止,或使用ExecutorService.shutdown()
/shutdownNow()
。
-
-
答题要点: 强调
BlockingQueue
是核心,完美契合此模型;说明生产者和消费者的基本操作;提到线程池管理消费者;简述关闭策略。
-
-
场景:如何实现一个简单的缓存 (Cache)?需要考虑缓存失效、淘汰策略、并发访问。
-
核心点: 快速访问、数据一致性(弱)、内存管理、并发。
-
方案:
-
数据结构:
ConcurrentHashMap
(核心存储)。考虑使用LinkedHashMap
(可支持 LRU) 或第三方库 (Guava Cache, Caffeine)。 -
基本操作 (
get(key)
,put(key, value)
): 直接操作 Map。 -
失效策略:
-
定时失效: 使用
ScheduledExecutorService
或Timer
(不推荐)定期扫描清除过期项。缺点: 不及时,占用资源。 -
惰性失效: 在
get(key)
时检查是否过期,过期则删除并返回 null/重新加载。缺点: 可能积累大量过期数据。 -
访问顺序失效 (LRU/LFU): 结合
LinkedHashMap
(覆盖removeEldestEntry
) 或使用支持淘汰策略的缓存库。最佳实践:使用库。
-
-
并发:
ConcurrentHashMap
保证并发安全。写操作较多时注意锁竞争。 -
淘汰策略 (Eviction Policy): 当缓存满时(或接近满时)需要淘汰数据。常见策略:
-
LRU (Least Recently Used): 淘汰最久未使用的。
-
LFU (Least Frequently Used): 淘汰使用频率最低的。
-
FIFO (First In First Out): 淘汰最早进入的。
-
Random: 随机淘汰。
-
-
高级考虑: 缓存击穿、雪崩、穿透的应对策略(加锁、布隆过滤器等)。
-
-
答题要点: 优先推荐使用成熟缓存库 (Caffeine > Guava Cache);说明核心使用
ConcurrentHashMap
;解释失效和淘汰策略的必要性和常见实现方式;提及缓存异常情况的应对(加分项)。
-
设计与建模场景
-
场景:设计一个停车场管理系统 (Parking Lot System)。
-
核心点: 对象建模、状态管理、规则处理。
-
关键类和对象:
-
ParkingLot
: 管理多个ParkingFloor
或多个ParkingSpot
。总容量,可用计数。 -
ParkingFloor
: (可选) 管理本层的ParkingSpot
。 -
ParkingSpot
: 停车位。属性:编号、类型(小型、中型、大型、残疾人位)、是否可用、关联的Vehicle
。 -
Vehicle
: 车辆。属性:车牌号、类型(对应停车位类型)。 -
Ticket
: 停车票。属性:票据号、入场时间、关联的车位号、关联的车辆车牌号。 -
EntryPanel
: 入口面板。分配车位、打印票。 -
ExitPanel
: 出口面板。计算费用、收费、释放车位。
-
-
核心流程:
-
入场 (
parkVehicle(Vehicle v)
):-
寻找可用且类型匹配的
ParkingSpot
。 -
标记车位为占用,关联车辆。
-
创建
Ticket
记录入场时间和车位。 -
更新
ParkingLot
可用计数。
-
-
出场 (
exitVehicle(Ticket t)
):-
根据
Ticket
找到车位和车辆。 -
计算停车费(基于入场时间和费率规则)。
-
收费。
-
释放车位(标记可用,解除车辆关联)。
-
更新
ParkingLot
可用计数。
-
-
-
答题要点: 清晰定义核心类和关系;描述入场和出场的关键步骤;考虑车位类型匹配;考虑并发访问(如多个入口/出口)时使用同步或并发集合;可扩展性(不同计费规则、多楼层)。
-
-
场景:设计一个简单的在线购物车 (Shopping Cart)。
-
核心点: 状态管理、会话管理、数据结构。
-
方案:
-
数据结构:
-
Cart
: 代表一个购物车。属性:用户标识(用户登录后关联用户ID;未登录用SessionID)、List<CartItem>
。 -
CartItem
: 购物车项。属性:商品ID (productId
)、商品名称、单价 (price
)、数量 (quantity
)、小计(可计算)。
-
-
核心操作:
-
addItem(Product product, int quantity)
: 添加商品项。如果购物车已有该商品,增加数量;否则新增项。 -
updateItemQuantity(String productId, int newQuantity)
: 修改某商品数量。数量为0时移除该项。 -
removeItem(String productId)
: 移除指定商品项。 -
getCartTotal()
: 计算购物车总金额(遍历所有CartItem
计算小计并求和)。 -
clearCart()
: 清空购物车。
-
-
存储:
-
用户未登录: 存储在
HttpSession
中(键值对,如session.setAttribute("cart", cartObject)
)。 -
用户已登录: 可以将会话中的购物车合并到数据库(关联用户ID)中,或直接从数据库加载。后续操作持久化到数据库。
-
-
并发考虑: 同一用户在不同设备/标签页操作购物车?通常通过用户会话隔离。数据库操作需考虑并发更新(乐观锁)。
-
其他: 商品价格变动如何处理?(通常下单时锁定价格,购物车展示实时价格但下单需校验)。
-
-
答题要点: 定义
Cart
和CartItem
结构;说明核心操作方法;区分登录/未登录状态的存储策略;简述价格一致性问题。
-
-
场景:设计一个日志记录器 (Logger)。要求支持不同级别(DEBUG, INFO, WARN, ERROR),可以输出到控制台或文件。
-
核心点: 设计模式(责任链、抽象工厂等)、扩展性、配置化。
-
方案 (参考 SLF4J/Logback 思想):
-
接口
Logger
:-
方法:
debug(String msg)
,info(String msg)
,warn(String msg)
,error(String msg)
,error(String msg, Throwable t)
。
-
-
日志级别
Level
:DEBUG < INFO < WARN < ERROR
。 -
抽象
Appender
(或Handler
): 负责日志的实际输出目的地。-
接口/抽象类:
append(LoggingEvent event)
。 -
具体实现:
ConsoleAppender
(输出到System.out
/System.err
),FileAppender
(输出到文件,需处理文件滚动、归档)。
-
-
Logger
实现:-
持有其日志级别 (
Level
)。 -
持有一个或多个
Appender
的引用。 -
当调用日志方法时(如
info(msg)
):-
检查请求级别是否 >= 该 Logger 设置的级别(如 Logger 是 INFO 级别,则 DEBUG 请求被忽略)。
-
如果通过,创建
LoggingEvent
对象(包含时间戳、线程名、Logger名、级别、消息、异常等)。 -
将
LoggingEvent
传递给所有关联的Appender
进行输出。
-
-
-
Logger
获取: 通常通过一个LoggerFactory
获取(单例或静态方法),工厂内部管理 Logger 实例(按名称缓存)。 -
配置: 通过配置文件(XML, Properties)设置 Logger 级别、Appender 类型和参数(文件路径、格式等)。
-
日志格式 (
Layout
/Formatter
): 可抽象一个组件负责将LoggingEvent
格式化成字符串(如PatternLayout
)。
-
-
答题要点: 强调职责分离(Logger 负责级别过滤和事件创建,Appender 负责输出);说明责任链模式的应用(Logger -> Appenders);提到配置化的重要性;参考主流日志框架设计。
-
性能与优化场景
-
场景:线上应用出现 OOM (OutOfMemoryError)。如何排查和定位问题?
-
核心点: JVM 内存模型、监控工具、分析工具、常见原因。
-
排查步骤:
-
确认错误类型:
java.lang.OutOfMemoryError: Java heap space
(堆内存不足) 最常见。还有PermGen space
(JDK7-, 方法区),Metaspace
(JDK8+, 元空间),Unable to create new native thread
(线程过多),GC overhead limit exceeded
(GC效率低下) 等。 -
分析堆内存 (Heap Dump):
-
在启动参数中添加
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/dump.hprof
。发生 OOM 时自动生成堆转储文件。 -
使用分析工具加载
.hprof
文件:MAT (Memory Analyzer Tool), VisualVM, JProfiler。 -
关键分析:
-
找出占用内存最大的对象是哪些? (MAT 的 Dominator Tree, Histogram)
-
这些对象被谁引用着?为什么 GC 回收不掉? (MAT 的 Path to GC Roots)
-
是否存在内存泄漏 (Memory Leak)? (对象本应被回收但因错误引用导致无法回收,数量持续增长)。查看对象的增长趋势。
-
-
-
分析 GC 日志:
-
添加参数
-Xloggc:/path/to/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps
。 -
使用 GCViewer, GCEasy 等工具分析。
-
关注点: GC 频率、Full GC 频率和耗时、每次 GC 后各区域大小变化、晋升老年代情况、是否因分配担保失败导致 OOM。
-
-
监控实时状态 (可选): 在 OOM 发生前或复现时使用
jconsole
,jvisualvm
,jstat -gcutil <pid> <interval>
监控内存、GC、线程状态。 -
常见原因:
-
内存泄漏: 静态集合类持有对象引用、未关闭资源(连接、流)、监听器未注销、ThreadLocal 使用不当未清理。
-
堆大小设置不合理 (
-Xmx
): 根本不够用。 -
加载过多数据到内存: 大文件、大查询结果未分页。
-
代码问题: 死循环创建对象、大对象(数组)频繁创建。
-
-
-
答题要点: 强调 Heap Dump 是关键;熟悉分析工具(MAT);知道如何配置生成 Dump 和 GC 日志;列举常见内存泄漏场景;区分不同 OOM 类型。
-
-
场景:发现某个接口响应时间变慢。如何定位性能瓶颈?
-
核心点: 性能分析工具、分层排查、监控指标。
-
排查步骤 (从应用层到基础设施层):
-
确认范围: 是所有请求慢,还是特定请求/参数慢?是持续慢还是偶发慢?
-
应用代码分析:
-
Profiling (剖析): 使用 VisualVM Profiler, JProfiler, Async Profiler, Arthas 进行 CPU 采样或方法耗时统计。找出 CPU 热点方法或阻塞点。
-
日志分析: 在关键路径添加详细耗时日志(使用
System.currentTimeMillis()
或StopWatch
, 或 MDC 记录 TraceId)。关注数据库查询、远程调用、复杂计算的耗时。 -
线程分析 (
jstack <pid>
): 查看线程状态。大量线程处于BLOCKED
,WAITING
,TIMED_WAITING
状态可能表示锁竞争或 IO 等待。死锁?
-
-
数据库分析:
-
慢查询日志: 检查数据库(MySQL, PostgreSQL 等)的慢查询日志。分析慢 SQL 的执行计划 (
EXPLAIN
/EXPLAIN ANALYZE
)。 -
索引: 检查是否缺少必要索引或索引失效。
-
连接池: 连接池是否耗尽?获取连接是否慢?
-
-
外部依赖分析:
-
远程调用 (RPC/HTTP): 检查下游服务的响应时间(分布式链路追踪如 SkyWalking, Zipkin)。下游服务是否慢?网络延迟?
-
缓存: 缓存命中率是否下降?缓存操作是否变慢?
-
消息队列: 生产/消费是否积压?
-
-
JVM 分析:
-
GC 日志分析 (同 OOM 排查): 频繁 Full GC 会导致 STW (Stop-The-World) 暂停,导致请求卡顿。
-
jstat -gcutil
: 实时查看 GC 情况。
-
-
系统资源监控:
-
CPU: 使用率是否过高?
top -Hp <pid>
看哪个线程 CPU 高。 -
内存: 是否充足?Swap 使用情况?
free -m
-
磁盘 IO: 是否瓶颈?
iostat
,iotop
-
网络 IO: 带宽是否打满?延迟是否高?
sar -n DEV
,ping
,traceroute
-
-
基础设施: 虚拟机负载、容器调度、网络设备。
-
-
答题要点: 强调分层次(代码->DB->外部->JVM->系统)逐步排查;熟练使用性能分析工具(Profiler, jstack, GC 日志分析);关注数据库和外部依赖;结合监控指标。
-
框架与系统场景
-
场景:在 Spring 项目中,如何设计一个权限控制系统?
-
核心点: 认证 (Authentication)、授权 (Authorization)、RBAC/ABAC、Spring Security。
-
方案 (基于 Spring Security):
-
认证 (Authn): 用户是谁?
-
实现
UserDetailsService
接口加载用户信息(用户名、密码、权限列表)。 -
配置认证方式(表单登录、Basic Auth、JWT、OAuth2)。
-
-
授权 (Authz): 用户能做什么?
-
基于角色 (RBAC):
hasRole('ADMIN')
,hasAnyRole('USER','EDITOR')
。 -
基于权限 (Permission):
hasAuthority('USER:READ')
,hasAuthority('USER:WRITE')
。更细粒度。 -
方法级安全: 在 Service 方法上使用
@PreAuthorize
,@PostAuthorize
,@Secured
注解。 -
URL 级安全: 在配置类 (
WebSecurityConfigurerAdapter
) 的configure(HttpSecurity http)
中使用.antMatchers(...).hasRole(...)
或.access("hasAuthority('...')")
。 -
动态权限: 实现
FilterInvocationSecurityMetadataSource
从数据库加载 URL-权限映射关系。实现AccessDecisionManager
或AccessDecisionVoter
进行投票决策。
-
-
RBAC 模型设计 (数据库):
-
User
(用户) -User_Role
(用户角色关联) -Role
(角色) -Role_Permission
(角色权限关联) -Permission
(权限:通常对应资源+操作,如user:delete
)。 -
权限可直接关联到角色,用户通过角色间接拥有权限。
-
-
会话管理: 有状态(Session) vs 无状态(JWT)。
-
-
答题要点: 区分认证和授权;介绍 Spring Security 核心组件(
UserDetailsService
,FilterChain
,AccessDecisionManager
);说明 RBAC 模型及其表结构;提到方法级和 URL 级配置;了解动态权限概念。
-
-
场景:设计一个分布式 ID 生成器 (Snowflake 算法思路)。
-
核心点: 全局唯一、趋势递增、高性能、高可用、分布式。
-
Snowflake 方案 (Twitter):
-
64位 ID 结构:
0 | 41位时间戳 (毫秒) | 5位数据中心ID | 5位机器ID | 12位序列号
-
组成部分:
-
符号位 (1 bit): 恒为0(表示正数)。
-
时间戳 (41 bits): 当前时间戳(毫秒级)减去一个自定义纪元 (
epoch
, 如2020-01-01
)。可用约 69 年。 -
数据中心ID (5 bits): 支持最多
2^5 = 32
个数据中心。 -
机器ID (5 bits): 每个数据中心支持最多
2^5 = 32
台机器。 -
序列号 (12 bits): 同一毫秒内同一机器上的计数器。支持每台机器每毫秒生成
2^12 = 4096
个 ID。
-
-
生成过程 (单机):
-
获取当前时间戳
timestamp
(毫秒)。 -
如果当前时间戳 < 上次生成的时间戳,说明时钟回拨,需要处理(等待、报错、使用备用机制)。
-
如果当前时间戳 == 上次生成的时间戳:
-
序列号
sequence
= (sequence
+ 1) &sequenceMask
(掩码,低12位全1)。 -
如果
sequence
== 0(表示当前毫秒序列号用完),则循环等待到下一毫秒,重置sequence
。
-
-
如果当前时间戳 > 上次生成的时间戳,重置
sequence
为 0(或一个随机起始值)。 -
记录上次时间戳
lastTimestamp
= 当前时间戳。 -
拼接各部分:
(timestamp - epoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence
。
-
-
优点: 本地生成,无中心节点,性能高;ID 趋势递增;可读性好(包含时间戳、机器信息)。
-
缺点: 依赖机器时钟(时钟回拨问题);机器 ID 需要配置(可通过 Zookeeper/Etcd 等分配)。
-
-
答题要点: 清晰描述 Snowflake 的 64 位结构;说明每一部分的含义和作用;详细描述生成算法的步骤(特别是时间戳比较和序列号处理);强调时钟回拨问题及其应对;知道其优缺点和适用场景。可提其他方案(UUID, Redis Incr, DB Ticket Table, Leaf, TinyId)做对比。
-
问题解决与行为场景
-
场景:你在代码审查 (Code Review) 中发现同事的代码存在潜在的性能问题(如循环内执行数据库查询)。你会如何处理?
-
核心点: 沟通技巧、团队协作、解决问题导向。
-
处理步骤:
-
私下沟通: 优先选择私下、友好的方式沟通(IM、面对面),避免在公共评论中直接指责。
-
描述事实,而非评价人: “我在看这块代码时,注意到在
for
循环里调用了userDao.getById()
,每次循环都会查一次数据库...”。 -
解释潜在影响: “这可能会在数据量大的时候导致数据库查询次数暴增,成为性能瓶颈。”
-
提出建设性建议: “我们可以考虑在循环外面一次性把需要的用户数据都查出来(比如根据 ID 列表批量查询
userDao.getByIds(List ids)
),然后在循环里使用内存数据。” 或者 “看看业务逻辑是否允许重构避免循环内查询?” -
提供依据/示例 (可选): 如果方便,可以分享一个优化后的伪代码片段或相关文档链接。
-
倾听对方观点: 了解同事当时的考虑,是否有特殊原因。
-
达成共识: 共同讨论最佳解决方案,并确认修改。
-
Code Review 工具中评论: 在确认沟通后,可以在 Review 工具中正式记录这个讨论和建议。
-
-
答题要点: 强调私下、友好、建设性;对事不对人;清晰指出问题+解释原因+给出建议;倾听和协作;最终目的是写出更好的代码。
-
-
场景:线上系统突然报警,CPU 使用率飙升到 100%。你作为值班人员,如何快速响应和排查?
-
核心点: 应急响应流程、故障排查思路、工具使用。
-
响应步骤:
-
确认报警: 登录监控系统查看具体指标(CPU、内存、网络、磁盘、GC、应用指标、上下游依赖)是否确实异常。排除误报。
-
初步定位影响范围: 是单台机器还是集群?影响哪些功能?用户感知如何?
-
保留现场 (关键): 尽快抓取能帮助定位问题的快照信息,避免重启后丢失:
-
CPU:
top -Hp <pid>
(找占用 CPU 高的线程 PID) -> 将 PID 转为 16 进制 ->jstack <pid> > jstack.log
(看该线程在做什么)。 -
内存:
jmap -dump:live,format=b,file=heap.hprof <pid>
(抓 Heap Dump,谨慎使用,可能 STW)。 -
GC:
jstat -gcutil <pid> 1000 5
(每隔1秒打印1次GC情况,共5次)。 -
线程:
jstack <pid> > jstack.log
(查看线程栈,找死锁、大量 WAITING/BLOCKED 线程)。
-
-
分析快照信息:
-
查看
top -Hp
和jstack
结果,定位热点线程在执行什么代码(看线程栈)。 -
如果是 Full GC 频繁导致,分析 GC 日志。
-
如果是死循环,查看对应代码。
-
-
尝试缓解 (如果可能且风险可控):
-
重启: 快速恢复服务的最常用方法(但会丢失现场,应在保留现场后做)。
-
回滚: 如果是最近发布导致,考虑回滚到上一个稳定版本。
-
扩容/隔离: 增加实例分担负载,或隔离问题实例。
-
限流/降级: 保护核心链路,避免雪崩。
-
-
深入排查根因: 利用保留的快照和日志,结合代码进行深入分析(如分析 Heap Dump)。
-
修复和恢复: 找到根因后,修复代码、配置或基础设施问题,并验证恢复。
-
复盘: 记录故障时间线、原因、处理过程、改进措施。
-
-
答题要点: 强调保留现场的重要性(
top -Hp
+jstack
是第一步);熟悉关键诊断命令;区分快速缓解措施和根因排查;体现应急处理流程(确认->止损->恢复->复盘)。
-
-
场景:项目需要引入一项你不太熟悉的新技术。你会如何着手学习和评估?
-
核心点: 学习能力、方法论、风险评估。
-
步骤:
-
明确需求和目标: 为什么需要这项技术?要解决什么具体问题?(性能提升、功能需求、降低成本?)替代现有方案的优势?
-
官方文档是起点: 阅读官方文档(Getting Started, Concepts, Reference),了解核心概念、特性和最佳实践。
-
实践出真知: 动手搭建 Demo 或 POC (Proof of Concept),验证核心功能是否满足需求,感受易用性。
-
社区与生态: 查看 GitHub Stars/Issues/PRs、Stack Overflow 活跃度、官方论坛。社区活跃度和支持度很重要。
-
学习资源: 查找高质量的博客、教程、视频课程、书籍(官方推荐或知名作者)。
-
对比评估: 如果有竞品,进行对比分析(功能、性能、成熟度、社区、学习曲线、License、与现有技术栈集成度)。
-
风险评估:
-
成熟度: 是稳定版还是 Beta/Alpha?生产环境使用案例?
-
学习曲线: 团队掌握需要多久?是否有专家?
-
维护成本: 升级、Bug 修复、社区支持力度。
-
兼容性: 与现有系统(JDK 版本、框架、中间件)是否兼容?
-
性能开销: 引入后对系统性能的影响?
-
-
分享与决策: 将学习成果、Demo、评估结果(特别是收益和风险)整理成文档或演示,与团队和 Leader 讨论,共同决策是否引入。
-
-
答题要点: 体现系统性学习路径(文档->实践->社区->资源);强调需求驱动;突出风险评估意识(成熟度、成本、兼容性);体现分享和团队协作。
-
Java面试场景题
基础概念类
解释Java中的多态性,并举例说明运行时多态的实现方式。
String、StringBuffer和StringBuilder的区别是什么?分别在什么场景下使用?
集合框架
HashMap的工作原理是什么?如何处理哈希冲突?
比较ArrayList和LinkedList的性能差异,说明各自适用场景。
并发编程
如何实现线程安全?列举至少三种方式并分析优缺点。
解释volatile关键字的作用,它与synchronized有何区别?
JVM相关
描述JVM内存模型,哪些区域是线程共享的?
哪些情况会导致Java内存泄漏?如何检测和避免?
设计模式
如何在Spring中实现单例模式?确保线程安全的方法有哪些?
观察者模式的适用场景是什么?请用代码示例说明实现方式。
异常处理
检查型异常和非检查型异常的区别?各自的处理策略是什么?
实战问题
系统出现OutOfMemoryError,有哪些排查步骤和解决方案?
如何设计一个分布式ID生成器?需要考虑哪些因素?
框架相关
Spring Bean的生命周期是怎样的?哪些扩展点可以干预这个过程?
MyBatis中#{}和${}的区别是什么?如何防止SQL注入?
算法与数据结构
实现LRU缓存机制,要求时间复杂度为O(1)。
找出数组中第K大的元素,至少给出两种解法并分析复杂度。
系统设计
设计一个秒杀系统,重点说明如何解决超卖和高并发问题。
如何保证微服务架构下的数据一致性?
每个问题都应包含:
- 核心概念清晰表达
- 实际应用场景说明
- 必要时提供代码片段或架构图
- 性能优化或异常处理方案
- 与其他技术的对比分析
Java面试场景题问题及答案
包含内容过多,只做了简单的章节截图介绍,每个章节都有更加细化的内容。