经纬恒润 Java开发 二面 面经

Java后端开发二面题目(完整版)

1. 说说Spring Boot的自动装配原理是如何实现的?

答案:

核心机制:

  • @SpringBootApplication包含三个注解
  • @EnableAutoConfiguration触发自动装配
  • @Import(AutoConfigurationImportSelector.class)导入配置

装配流程:

  1. 读取META-INF/spring.factories文件
  2. 加载所有EnableAutoConfiguration配置类
  3. 根据@Conditional条件注解过滤
  4. 将符合条件的Bean注册到容器

常见条件注解:

  • @ConditionalOnClass:类路径存在某个类
  • @ConditionalOnMissingBean:容器中没有某个Bean
  • @ConditionalOnProperty:配置文件有某个属性

自定义starter:

  • 创建配置类加@Configuration
  • spring.factories中注册
  • 使用条件注解控制加载

2. Redis的数据结构有哪些?分别适用什么场景?

答案:

String(字符串):

  • 场景:缓存、计数器、分布式锁
  • 命令:SET、GET、INCR、DECR

Hash(哈希):

  • 场景:存储对象、购物车
  • 命令:HSET、HGET、HMGET
  • 优势:节省内存,部分更新

List(列表):

  • 场景:消息队列、时间线、最新列表
  • 命令:LPUSH、RPUSH、LPOP、RPOP
  • 特点:有序、可重复

Set(集合):

  • 场景:标签、共同好友、去重
  • 命令:SADD、SMEMBERS、SINTER
  • 特点:无序、不重复

ZSet(有序集合):

  • 场景:排行榜、延迟队列
  • 命令:ZADD、ZRANGE、ZRANK
  • 特点:按score排序

其他类型:

  • Bitmap:签到、布隆过滤器
  • HyperLogLog:UV统计
  • GEO:地理位置
  • Stream:消息队列(5.0+)

3. 分布式事务有哪些解决方案?各有什么优缺点?

答案:

1. 2PC(两阶段提交)

  • 准备阶段:协调者询问参与者
  • 提交阶段:统一提交或回滚
  • 缺点:同步阻塞、单点故障、数据不一致

2. 3PC(三阶段提交)

  • 增加CanCommit阶段
  • 超时机制
  • 缺点:复杂度高

3. TCC(Try-Confirm-Cancel)

  • Try:预留资源
  • Confirm:确认提交
  • Cancel:取消回滚
  • 优点:性能好、无锁
  • 缺点:业务侵入性强

4. 本地消息表

  • 业务和消息在同一事务
  • 定时扫描发送消息
  • 优点:实现简单
  • 缺点:依赖定时任务

5. 消息队列(最终一致性)

  • 发送消息到MQ
  • 消费者处理业务
  • 优点:解耦、高性能
  • 缺点:只能保证最终一致

6. Seata(推荐)

  • AT模式:自动补偿
  • TCC模式:手动补偿
  • SAGA模式:长事务
  • 优点:对业务侵入小

选型建议:

  • 强一致性:TCC、Seata AT
  • 最终一致性:消息队列
  • 简单场景:本地消息表

4. JVM内存模型是怎样的?如何排查内存溢出问题?

答案:

内存区域:

线程共享:

  • 堆(Heap):对象实例、数组
  • 方法区(元空间):类信息、常量、静态变量

线程私有:

  • 虚拟机栈:局部变量、方法调用
  • 本地方法栈:Native方法
  • 程序计数器:字节码行号

常见OOM:

1. 堆溢出(最常见)

  • 原因:对象过多、内存泄漏
  • 错误:java.lang.OutOfMemoryError: Java heap space
  • 排查: jmap -heap pid查看堆使用jmap -dump:format=b,file=heap.hprof pid导出堆MAT工具分析dump文件

2. 栈溢出

  • 原因:递归太深、线程过多
  • 错误:StackOverflowError
  • 解决:增加栈大小-Xss

3. 元空间溢出

  • 原因:类加载过多
  • 错误:OutOfMemoryError: Metaspace
  • 解决:增加元空间-XX:MetaspaceSize

排查步骤:

  1. 查看GC日志:-XX:+PrintGCDetails
  2. 使用jstat监控:jstat -gc pid 1000
  3. dump堆分析:找到占用内存最多的对象
  4. 分析代码:是否有内存泄漏

预防措施:

  • 合理设置堆大小
  • 及时释放资源
  • 使用对象池
  • 避免大对象

5. MySQL的事务隔离级别有哪些?如何解决幻读问题?

答案:

四种隔离级别:

1. 读未提交(Read Uncommitted)

  • 可以读到未提交的数据
  • 问题:脏读、不可重复读、幻读

2. 读已提交(Read Committed)

  • 只能读到已提交的数据
  • 问题:不可重复读、幻读
  • Oracle默认级别

3. 可重复读(Repeatable Read)

  • 同一事务内多次读取结果一致
  • 问题:幻读
  • MySQL默认级别

4. 串行化(Serializable)

  • 完全串行执行
  • 无并发问题,性能最差

并发问题:

  • 脏读:读到未提交的数据
  • 不可重复读:两次读取数据不一致
  • 幻读:两次查询记录数不一致

MySQL如何解决幻读:

MVCC(多版本并发控制):

  • 每行记录有隐藏字段:事务ID、回滚指针
  • 读取时根据ReadView判断可见性
  • 快照读不会产生幻读

Next-Key Lock(间隙锁):

  • 锁定记录+间隙
  • 防止其他事务插入
  • 当前读(SELECT FOR UPDATE)使用

示例:

-- 事务A
BEGIN;
SELECT * FROM table WHERE id > 10 FOR UPDATE;
-- 锁定id>10的记录和间隙,事务B无法插入

-- 事务B
INSERT INTO table VALUES(15, ...); -- 阻塞

6. 如何设计一个秒杀系统?需要考虑哪些问题?

答案:

核心问题:

  • 高并发:瞬间大量请求
  • 超卖:库存扣减不一致
  • 恶意请求:刷单、爬虫

架构设计:

前端优化:

  • 按钮置灰,防止重复点击
  • 验证码、滑块验证
  • 静态资源CDN加速

接口层:

  • Nginx限流:限制单IP请求频率
  • 网关限流:令牌桶、漏桶算法
  • 接口防刷:用户维度限流

服务层:

1. 缓存预热

  • 提前将商品信息加载到Redis
  • 库存放Redis,减少DB压力

2. 库存扣减

// Redis原子操作
Long stock = redisTemplate.opsForValue().decrement("stock:" + goodsId);
if (stock < 0) {
    // 库存不足
    return;
}

3. 异步下单

  • 扣减库存成功后发送MQ消息
  • 消费者异步创建订单
  • 削峰填谷

4. 分布式锁

  • 防止超卖
  • Redisson实现
RLock lock = redisson.getLock("lock:goods:" + goodsId);
try {
    lock.lock();
    // 扣减库存
} finally {
    lock.unlock();
}

数据库层:

  • 读写分离
  • 分库分表
  • 乐观锁更新:
UPDATE goods SET stock = stock - 1 
WHERE id = ? AND stock > 0

监控告警:

  • 实时监控QPS、响应时间
  • 异常自动告警
  • 熔断降级

防止超卖方案:

  1. Redis预扣库存
  2. 数据库乐观锁兜底
  3. 定时对账,补偿机制

7. Spring AOP的实现原理是什么?动态代理有哪几种方式?

答案:

AOP核心概念:

  • 切面(Aspect):横切关注点的模块化
  • 连接点(JoinPoint):方法执行点
  • 切点(Pointcut):匹配连接点的表达式
  • 通知(Advice):在切点执行的动作
  • 织入(Weaving):将切面应用到目标对象

通知类型:

  • @Before:前置通知
  • @After:后置通知
  • @AfterReturning:返回后通知
  • @AfterThrowing:异常通知
  • @Around:环绕通知(最强大)

动态代理方式:

1. JDK动态代理

  • 基于接口
  • 使用Proxy.newProxyInstance()
  • 实现InvocationHandler接口
  • Spring默认方式(有接口时)

2. CGLIB代理

  • 基于继承
  • 生成目标类的子类
  • 使用字节码技术
  • 无接口时使用

区别:

  • JDK代理:只能代理接口,性能略好
  • CGLIB:可以代理类,final方法无法代理

应用场景:

  • 日志记录
  • 权限校验
  • 事务管理(@Transactional
  • 性能监控
  • 异常处理

8. 如何保证Redis和MySQL的数据一致性?

答案:

常见问题:

  • 更新数据库后,缓存未更新
  • 并发情况下数据不一致

方案对比:

1. 先更新数据库,再删除缓存(推荐)

// 更新数据库
userMapper.update(user);
// 删除缓存
redisTemplate.delete("user:" + userId);

  • 优点:简单可靠
  • 问题:并发时可能读到旧数据

2. 延迟双删

// 删除缓存
redisTemplate.delete("user:" + userId);
// 更新数据库
userMapper.update(user);
// 延迟删除缓存
Thread.sleep(500);
redisTemplate.delete("user:" + userId);

  • 解决并发问题
  • 缺点:延迟时间难确定

3. 先删除缓存,再更新数据库

  • 问题:缓存失效期间,大量请求打到DB

4. 更新数据库,更新缓存

  • 问题:并发时可能缓存数据错误
  • 浪费资源(缓存可能不会被读取)

5. Canal监听binlog(最佳)

  • Canal伪装成MySQL从库
  • 监听binlog变化
  • 异步更新Redis
  • 优点:解耦、可靠
  • 缺点:有延迟

6. 设置过期时间兜底

  • 即使不一致,过期后会重新加载
  • 保证最终一致性

选型建议:

  • 简单场景:先更新DB,再删缓存
  • 高并发:延迟双删 + 过期时间
  • 复杂场景:Canal + MQ

9. 说说你对微服务的理解,服务之间如何通信?

答案:

微服务特点:

  • 单一职责,服务独立
  • 独立部署、独立扩展
  • 去中心化治理
  • 技术栈多样化

Spring Cloud组件:

注册中心:

  • Eureka、Nacos、Consul
  • 服务注册与发现

配置中心:

  • Config、Nacos
  • 统一配置管理、动态刷新

负载均衡:

  • Ribbon(客户端)
  • 轮询、随机、权重等策略

服务调用:

  • Feign:声明式HTTP客户端
  • RestTemplate + Ribbon

熔断降级:

  • Hystrix、Sentinel
  • 防止雪崩效应

网关:

  • Gateway、Zuul
  • 路由、限流、鉴权

链路追踪:

  • Sleuth + Zipkin
  • 分布式追踪

服务通信方式:

1. HTTP/REST(常用)

  • Feign调用
  • 简单直观
  • 性能一般

2. RPC

  • Dubbo、gRPC
  • 性能高
  • 二进制协议

3. 消息队列

  • 异步通信
  • 解耦
  • 削峰填谷

选型:

  • 同步调用:Feign/Dubbo
  • 异步通知:MQ
  • 实时性要求高:RPC

10. 如何设计一个高可用的系统?

答案:

高可用目标:

  • 99.9%:年故障时间8.76小时
  • 99.99%:年故障时间52.56分钟
  • 99.999%:年故障时间5.26分钟

设计原则:

1. 消除单点故障

  • 服务多实例部署
  • 数据库主从复制
  • Redis哨兵/集群
  • 负载均衡

2. 故障隔离

  • 服务拆分,避免级联失败
  • 线程池隔离
  • 熔断降级

3. 限流保护

  • 网关层限流
  • 服务层限流
  • 数据库连接池限制

4. 降级策略

  • 非核心功能降级
  • 返回默认值
  • 读缓存兜底

5. 超时控制

  • 设置合理超时时间
  • 快速失败
  • 避免资源占用

6. 重试机制

  • 幂等性保证
  • 指数退避
  • 最大重试次数

7. 监控告警

  • 实时监控关键指标
  • 异常自动告警
  • 日志收集分析

8. 灰度发布

  • 小流量验证
  • 逐步放量
  • 快速回滚

9. 容灾备份

  • 数据定期备份
  • 异地多活
  • 灾难恢复预案

10. 压测验证

  • 定期压测
  • 找到系统瓶颈
  • 容量规划

11. 说说你对设计模式的理解,项目中用过哪些?

答案:

常用设计模式:

1. 单例模式

  • 场景:Spring Bean默认单例
  • 实现:枚举、静态内部类

2. 工厂模式

  • 场景:BeanFactory创建Bean
  • 解耦对象创建

3. 代理模式

  • 场景:AOP、事务管理
  • JDK动态代理、CGLIB

4. 模板方法模式

  • 场景:JdbcTemplate、RestTemplate
  • 定义算法骨架,子类实现细节

5. 策略模式

  • 场景:支付方式选择、优惠计算
  • 避免大量if-else

6. 观察者模式

  • 场景:Spring事件机制
  • 发布订阅

7. 责任链模式

  • 场景:Filter链、拦截器
  • 请求依次处理

8. 装饰器模式

  • 场景:IO流包装
  • 动态添加功能

9. 适配器模式

  • 场景:SpringMVC的HandlerAdapter
  • 接口转换

10. 建造者模式

  • 场景:Lombok的@Builder
  • 构建复杂对象

项目实践:

  • 策略模式:不同支付方式
  • 工厂模式:创建不同类型的订单
  • 模板方法:导出不同格式文件
  • 责任链:权限校验链

设计原则:

  • 单一职责
  • 开闭原则
  • 里氏替换
  • 依赖倒置
  • 接口隔离

12. 项目中遇到过哪些性能问题?如何优化的?

答案:

问题1:接口响应慢

排查:

  • 查看日志,定位慢的环节
  • SQL慢查询日志
  • Arthas监控方法耗时

优化:

  • 添加索引,SQL优化
  • 增加Redis缓存
  • 异步处理非核心逻辑
  • 效果:500ms → 50ms

问题2:数据库连接池耗尽

原因:

  • 连接未释放
  • 并发量大,连接数不够

优化:

  • 检查代码,确保连接关闭
  • 增加连接池大小
  • 优化慢SQL,减少连接占用时间

问题3:内存溢出

排查:

  • jmap导出堆dump
  • MAT分析,发现大对象

优化:

  • 分批处理数据
  • 及时释放资源
  • 调整堆大小

问题4:缓存击穿

现象:

  • 热点key过期,大量请求打到DB

优化:

  • 热点数据不过期
  • 加互斥锁,只有一个请求查DB
  • 提前异步刷新缓存

问题5:GC频繁

排查:

  • jstat查看GC情况
  • GC日志分析

优化:

  • 调整堆大小
  • 优化代码,减少对象创建
  • 使用对象池

总结:

  • 先定位问题,再针对性优化
  • 使用工具辅助分析
  • 优化后验证效果

Java面试圣经 文章被收录于专栏

Java面试圣经,带你练透java圣经

全部评论
向大佬学习学习
点赞 回复 分享
发布于 02-19 22:46 河南

相关推荐

点赞 评论 收藏
分享
评论
2
13
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务