25年10月乐精灵 JAVA开发 面经
#JAVA##JAVA面经##JAVA内推#
先简单聊聊自己吧~学校、专业,怎么和编程结缘的?
“我是XX大学计算机科学与技术专业大四学生。高中因信息学竞赛接触C++,大学用Java写第一个‘课程表查询系统’时被其严谨的面向对象设计和‘一次编写,到处运行’的特性吸引。选择Java开发,源于三点:一是企业级应用对稳定性的高要求(如金融、电商),二是Spring生态对工程化的极致追求(从Boot到Cloud),三是在校园项目中用Java解决真实问题的成就感——比如用线程池优化判题服务,让同学提交代码后秒级反馈。我享受用代码构建可靠系统的踏实感,也渴望在贵司这样的平台深耕后端技术。”
挑一个你最有感触的项目:你负责哪块?当时遇到啥小难题?怎么搞定的?
“校园OJ系统中,我负责判题核心模块。
- 难题:高并发下判题服务CPU飙升(同学用死循环耗尽资源)
- 解决:
1️⃣ 硬隔离:Docker容器限制--cpus=0.5 --memory=128m
2️⃣ 软监控:Shell脚本定时检测进程CPU,超阈值自动kill
3️⃣ 流程加固:RabbitMQ消费前校验代码特征(如检测while(true))- 收获:安全与稳定性必须前置设计。这件事让我养成‘上线前必压测+监控覆盖’的习惯。”
抽奖小场景:如何让每次抽奖概率尽量一致?
“核心思路:避免分次独立抽样。
✅ 推荐方案:后台一次性随机生成2个不重复中奖者(如Collections.shuffle(users).subList(0,2)),前端分两次‘公布结果’。
- 优势:
• 每人中奖概率严格=2/10
• 无概率偏差(避免1/10→1/9的波动)
• 业务可解释(‘系统已抽完,分步揭晓’)
⚠️ 若强制分两次抽:需用‘有放回抽样+去重重抽’,但实现复杂且体验割裂。工程上,用技术手段保障公平性,而非迁就业务表述。”
项目里用RabbitMQ主要是为了解决啥问题?
“三大价值:
🔹 削峰:秒杀请求入队,判题服务匀速消费(峰值QPS 500→稳定100)
🔹 解耦:订单创建后,异步触发短信通知、积分更新,避免主流程阻塞
🔹 异步:用户提交代码后立即返回‘排队中’,判题结果通过WebSocket推送
项目实例:OJ系统中,判题服务宕机时,消息积压在队列,恢复后自动续处理,保障用户体验。”
消息怎么保证不丢?
“三端可靠性设计:
🔹 生产者:Confirm模式 + 异步监听ack/nack → nack时存DB + 定时补偿重发
🔹 Broker:队列/交换机/消息均持久化(durable=true)
🔹 消费者:手动ACK(处理成功后ack) + 幂等设计(业务ID去重) + 死信队列(DLQ)兜底
项目验证:压测10万消息,丢失率=0。关键认知:可靠性是组合拳,单点保障必有漏洞。”
中奖后半小时内领奖,超时自动失效——怎么实现?
“三方案对比:
方案 实现 适用场景 延迟队列 RabbitMQ TTL + 死信队列 高可靠、需精确触发 Redis ZSet score=过期时间戳,定时扫描 轻量、高并发 定时任务 每分钟查DB超时记录 简单业务 项目选择:用Redis ZSet(见下题),因判题超时场景需高频扫描,ZSet范围查询效率O(logN)。”
不用MQ,只用Redis(ZSet)+MySQL实现半小时失效?
“四步闭环:
1️⃣ 中奖时:
- MySQL存中奖记录(status=UNCLAIMED)
- Redis ZSet:
ZADD award_expire <timestamp+1800> award_id
2️⃣ 后台线程:每秒执行ZRANGEBYSCORE award_expire 0 <current_timestamp>
3️⃣ 处理失效:- 遍历返回的award_id,更新MySQL status=EXPIRED
ZREM award_expire award_id
4️⃣ 领奖时:先查ZSet是否存在,存在则删除并更新DB
优势:避免数据库轮询压力,Redis原子操作保障一致性。”
MySQL事务的ACID,结合项目说说哪一点你特别注意过?
“一致性(Consistency) 是生命线。
- 场景:二手平台‘下单扣库存’
- 保障:
@Transactional(rollbackFor = Exception.class) public void createOrder(Order order) { // 1. 扣库存(乐观锁:version校验) boolean success = productMapper.decrStock(order.getProductId(), order.getCount()); if (!success) throw new BusinessException("库存不足"); // 2. 创建订单 orderMapper.insert(order); // 3. 发送MQ(异步,不阻塞事务) rabbitTemplate.convertAndSend("order.queue", order); }- 反思:曾因漏加
rollbackFor,业务异常未回滚导致超卖。从此将‘事务边界+回滚规则’纳入Code Review Checklist。”
InnoDB怎么保证“原子性”?
“靠 Undo Log:
- 事务修改数据前,将旧值写入Undo Log(存于共享表空间)
- 回滚时:根据Undo Log逆向操作(如INSERT→DELETE)
- 关键细节:Undo Log本身也受Redo Log保护,崩溃恢复时先重做Redo,再用Undo回滚未提交事务
认知:原子性不是‘魔法’,而是日志驱动的精密工程。”
事务隔离级别?项目用哪个?为啥?
“项目用 REPEATABLE_READ(InnoDB默认):
- 原因:
✅ 避免不可重复读(如订单查询时价格突变)
✅ InnoDB通过间隙锁(Gap Lock) 解决幻读(范围查询加锁)
✅ 平衡性能与一致性(SERIALIZABLE锁表,性能差)- 避坑:明确告知前端‘列表页数据可能非实时’,关键操作(如支付)加版本号校验。”
“幻读”问题?InnoDB在RR级别下怎么防?
“幻读定义:同一事务内,两次范围查询,第二次查到第一次不存在的记录(如
SELECT * WHERE id>10,中间被插入id=11)。
InnoDB解决方案:
- 间隙锁(Gap Lock):锁住记录之间的‘间隙’(如id=10和id=20之间),阻止插入
- 临键锁(Next-Key Lock):记录锁 + 间隙锁组合
注意:RR级别下,当前读(如SELECT ... FOR UPDATE)会加间隙锁防幻读;快照读(普通SELECT)靠MVCC避免幻读感知。”
MVCC能用大白话说说吗?
“像‘时光机’:
1️⃣ 每行数据有隐藏字段:trx_id(创建事务ID)、roll_pointer(指向Undo Log旧版本)
2️⃣ 事务开始时生成Read View(记录当前活跃事务ID列表)
3️⃣ 查询时:
- 只返回
trx_id< Read View最小值 且 未删除的版本- 若当前版本不可见,沿
roll_pointer找历史版本
✅ 效果:读操作无锁,不同事务‘看到’各自时间点的数据快照
项目价值:OJ系统中,学生查提交记录时,即使判题服务正在更新状态,也能看到稳定历史结果。”
索引在项目里怎么用的?像书的目录有啥好处?
“项目实例:
- 为
orders(user_id, create_time)建联合索引- 好处:
✅ 快速定位:查‘用户近7天订单’,避免全表扫描(10万行→扫15行)
✅ 避免回表:查询字段全在索引中(覆盖索引)
✅ 排序优化:ORDER BY create_time直接走索引
比喻:没有索引=翻整本字典找‘苹果’;有索引=直接翻‘苹’字部,效率天壤之别。”
聚簇索引是啥?和普通索引区别?
"| 特性 | 聚簇索引(主键索引) | 普通索引(二级索引) |
|------|---------------------|---------------------|
| 叶子节点 | 存整行数据 | 存主键值 |
| 查询 | 一次IO(无需回表) | 两次IO(回表查聚簇索引) |
| 数量 | 每表仅1个 | 可多个 |
项目认知:设计表时,主键选自增ID(避免页分裂),业务查询用二级索引+覆盖索引优化。”
为啥MySQL爱用B+树做索引
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
本专栏在精不在多,内容分为八股文、大厂真实面经,面试通过后将offer和面试题私发给我,可退还专栏的收益部分费用。欢迎大家共建专栏