现在 Java 面试确实早已从死记硬背的 “八股文” 转向了场景化、实战化的问题考察,核心是检验你解决实际业务问题的能力,而不是单纯的知识点记忆。没真正做过相关项目的话,这类问题确实很难答到点子上。

下面我整理了几个高频的 Java 面试场景题(覆盖核心业务场景 + 技术选型 + 问题排查),并给出有落地性的回答思路和核心要点,帮你理解这类问题的答题逻辑:

场景题 1:高并发接口优化场景

问题:你负责的接口在高峰期 QPS 达到 5000,出现了响应超时、数据库连接池打满的问题,你会怎么分析和优化?

回答思路(分步骤,体现实战思维)
  1. 问题定位(先诊断,再优化)

    • 先通过监控工具(Prometheus/Grafana、SkyWalking)确认瓶颈:是 CPU / 内存瓶颈?数据库慢查询?还是网络 / 线程池问题?
    • 用show processlist查数据库慢 SQL,用 Arthas 排查 JVM 线程状态(是否有大量 WAITING 线程)、接口耗时分布。
  2. 分层优化(从易到难)

    // 示例:核心优化点代码落地
    // 1. 接口层:增加本地缓存(Guava Cache)+ 分布式缓存(Redis)
    @Service
    public class OrderService {
        // 本地缓存:适合高频读、低频写的热点数据
        private final LoadingCache<Long, OrderDTO> orderCache = CacheBuilder.newBuilder()
                .maximumSize(10000) // 缓存最大数量
                .expireAfterWrite(5, TimeUnit.MINUTES) // 5分钟过期
                .build(new CacheLoader<Long, OrderDTO>() {
                    @Override
                    public OrderDTO load(Long orderId) {
                        // 缓存未命中时查询数据库
                        return orderMapper.selectById(orderId);
                    }
                });
    
        // 分布式缓存:适合集群环境下的热点数据
        @Autowired
        private StringRedisTemplate redisTemplate;
    
        public OrderDTO getOrder(Long orderId) {
            // 1. 查本地缓存
            try {
                return orderCache.get(orderId);
            } catch (Exception e) {
                // 本地缓存异常,降级查Redis
                String key = "order:" + orderId;
                String json = redisTemplate.opsForValue().get(key);
                if (StringUtils.isNotBlank(json)) {
                    return JSON.parseObject(json, OrderDTO.class);
                }
                // Redis未命中,查数据库并回写缓存
                OrderDTO order = orderMapper.selectById(orderId);
                redisTemplate.opsForValue().set(key, JSON.toJSONString(order), 30, TimeUnit.MINUTES);
                return order;
            }
        }
    }
    
    • 数据库层:慢 SQL 优化(加索引、拆分大 SQL)、读写分离、分库分表;
    • 应用层:线程池参数调优(核心线程数 = CPU 核心数 * 2,最大线程数根据业务调整)、接口异步化(CompletableFuture)、限流降级(Sentinel/Resilience4j)。
  3. 兜底方案:接口熔断、降级返回默认数据,避免服务雪崩。

场景题 2:分布式事务场景

问题:电商场景中,下单时需要扣减库存、生成订单、扣减用户积分,三个操作涉及不同服务 / 数据库,如何保证数据一致性?

回答思路(拒绝只说 “2PC/3PC”,要结合业务选型)
  1. 先明确业务优先级:核心诉求是 “订单必须生成,库存不能超卖,积分允许最终一致”,所以不用强一致性方案(性能差),优先选最终一致性方案。
  2. 落地方案:基于可靠消息的最终一致性(本地消息表 + MQ)
    // 1. 下单接口:先落本地消息表,再执行业务,最后发送MQ
    @Transactional(rollbackFor = Exception.class)
    public void createOrder(OrderCreateReq req) {
        // 步骤1:扣减库存(本地事务)
        stockService.deductStock(req.getSkuId(), req.getNum());
        // 步骤2:生成订单(本地事务)
        OrderDO order = new OrderDO();
        orderMapper.insert(order);
        // 步骤3:写入本地消息表(和订单同事务)
        MessageDO message = new MessageDO();
        message.setBizId(order.getId());
        message.setBizType("ORDER_CREATE");
        message.setContent(JSON.toJSONString(req));
        message.setStatus(0); // 0-待发送 1-已发送 2-已确认
        messageMapper.insert(message);
        // 步骤4:发送MQ(即使失败,有定时任务兜底)
        try {
            mqProducer.send("order_topic", JSON.toJSONString(message));
        } catch (Exception e) {
            // 发送失败不回滚订单,靠定时任务重试
            log.error("发送MQ失败,bizId:{}", order.getId(), e);
        }
    }
    
    // 2. 定时任务:重试发送未成功的消息(补偿机制)
    @Scheduled(fixedRate = 60000) // 每分钟执行
    public void retrySendMessage() {
        List<MessageDO> list = messageMapper.selectByStatusAndTimeout(0, 5); // 查5分钟前未发送的消息
        for (MessageDO msg : list) {
            try {
                mqProducer.send("order_topic", JSON.toJSONString(msg));
                // 发送成功,更新状态
                messageMapper.updateStatus(msg.getId(), 1);
            } catch (Exception e) {
                log.error("重试发送消息失败,msgId:{}", msg.getId(), e);
            }
        }
    }
    
    // 3. 积分服务消费MQ:扣减积分,消费成功后回调确认
    @RabbitListener(queues = "order_topic")
    public void handleOrderMessage(String msg) {
        MessageDO message = JSON.parseObject(msg, MessageDO.class);
        try {
            // 扣减积分
            pointService.deductPoint(message.getBizId(), JSON.parseObject(message.getContent(), OrderCreateReq.class).getUserId());
            // 回调订单服务,确认消息已消费
            orderService.confirmMessage(message.getId());
        } catch (Exception e) {
            // 消费失败,MQ会重试(配置重试次数,超过则进入死信队列)
            throw new AmqpRejectAndDontRequeueException("消费失败,进入死信队列");
        }
    }
    
  3. 备选方案:如果是核心金融场景(如转账),用 Seata 的 TCC 模式(Try-Confirm-Cancel),但要注意实现幂等性。

场景题 3:内存泄漏排查场景

问题:线上服务运行一周后,出现 Full GC 频繁、内存占用持续升高的问题,你怎么定位和解决?

回答思路(体现实操步骤)
  1. 获取堆快照:用jmap -dump:format=b,file=heap.hprof <pid>导出堆快照;
  2. 分析快照:用 MAT(Memory Analyzer Tool)打开,看Dominator Tree(支配树),找到占用内存最多的对象;
  3. 定位代码问题:
    • 常见原因:静态集合(如 static Map)未清理、线程池核心线程数设置过大且持有大对象、资源未关闭(IO / 连接);
    • 示例:排查到 static Map 缓存了用户会话,但只加不删,导致内存泄漏:
    // 错误代码
    public class SessionManager {
        // 静态Map,会话只添加不删除,内存持续上涨
        private static Map<String, UserSession> sessionMap = new HashMap<>();
    
        public void addSession(String sessionId, UserSession session) {
            sessionMap.put(sessionId, session);
        }
        // 缺少删除过期会话的方法
    }
    
    // 修复代码:使用带过期时间的缓存,自动清理
    public class SessionManager {
        private static LoadingCache<String, UserSession> sessionCache = CacheBuilder.newBuilder()
                .expireAfterAccess(2, TimeUnit.HOURS) // 2小时未访问自动过期
                .maximumSize(100000) // 最大缓存数,超过自动淘汰
                .build(new CacheLoader<String, UserSession>() {
                    @Override
                    public UserSession load(String sessionId) {
                        return null;
                    }
                });
    
        public void addSession(String sessionId, UserSession session) {
            sessionCache.put(sessionId, session);
        }
    
        public UserSession getSession(String sessionId) {
            return sessionCache.getIfPresent(sessionId);
        }
    }
    
  4. 验证修复:发布修复代码后,监控 JVM 内存趋势,观察 Full GC 频率是否降低。

总结

应对 Java 场景面试题的核心逻辑:

  1. 先诊断,再优化:任何问题先通过工具定位瓶颈,而不是上来就说 “加缓存 / 加机器”;
  2. 结合业务选型:技术方案没有最优,只有最适合(比如高并发读选缓存,分布式事务优先最终一致性);
  3. 落地到代码 / 操作步骤:回答时要说出具体的工具(Arthas/MAT)、代码思路(缓存怎么加、MQ 怎么用)、兜底方案(限流 / 降级),体现你真的做过。

Java面试场景题

需要Java面试场景题学习的小伙伴,查看下方名片拿走吧!

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐