一、 Java 基础与集合框架深度String、StringBuilder 与 StringBuffer 的区别及底层原理问题 创建了几个对象String 为什么是不可变的参考答案对象创建创建了 2 个对象。一个是字符串常量池中的 另一个是堆内存中通过 创建的 String 对象。不可变性String 类被 修饰且其内部存储字符的数组 也是 修饰的一旦初始化后引用不可变。这保证了多线程安全和字符串常量池的可靠性。线程安全StringBuffer 的核心方法加了 是线程安全的但性能较低StringBuilder 没有同步锁性能最高但线程不安全。HashMap 底层原理、扩容机制与 Key 的要求问题JDK 1.8 中 HashMap 的底层数据结构是什么为什么链表长度大于 8 时转为红黑树参考答案底层结构数组 链表 红黑树。树化原因基于泊松分布在理想哈希下链表长度达到 8 的概率极低。当发生严重哈希碰撞时链表查询时间复杂度会退化为 O(n)转为红黑树后可优化为 O(log n)。阈值设定转为红黑树的阈值是 8退化回链表的阈值是 6。这是因为红黑树节点占用空间是链表节点的 2 倍保留 6 是为了避免频繁的树化与退化转换。Key 的要求作为 Key 的对象必须正确重写 和 方法。如果只重写 而 使用 Object 默认的内存地址计算会导致存入和取出时哈希桶位置不一致从而无法获取数据。HashMap 的扩容机制与容量设计问题HashMap 什么时候会进行扩容为什么容量必须是 2 的幂次方参考答案扩容触发当 阈值 容量 × 负载因子默认 0.75时触发扩容。扩容会将容量翻倍并将旧数组元素重新 Hash 到新数组Rehash。2的幂次方原因当容量为 2 的幂次方时 等价于 。位运算效率远高于取模运算且能保证哈希分布均匀。并发集合 ConcurrentHashMap问题ConcurrentHashMap 在 JDK 1.7 和 1.8 中的实现有何不同参考答案JDK 1.7采用分段锁Segment ReentrantLock每个 Segment 管理一段哈希桶并发度取决于 Segment 的数量。JDK 1.8放弃了 Segment采用 数组。锁粒度细化到了单个哈希桶的头节点。并发插入时若桶为空使用 CAS若桶非空则使用 锁住头节点大幅提升了并发性能。点击下方小卡片免费获取Java资料Java面试资料https://mp.weixin.qq.com/s/iBmSycbmeTCSRNKJQ10EuA二、 并发编程与锁机制线程安全的实现方式问题Java 里面如何实现线程安全参考答案互斥同步使用 、 等加锁机制保证同一时刻只有一个线程访问共享资源。非阻塞同步基于冲突检测的乐观并发策略如 CASCompare And Swap操作和 原子类。无同步方案使用不可变对象如 String、线程本地存储ThreadLocal或栈封闭方法内的局部变量。synchronized 底层原理与锁升级问题 底层是怎么实现的偏向锁、轻量级锁了解吗参考答案底层实现JVM 通过对象头中的 Mark Word 记录锁状态。同步代码块通过 和 指令实现同步方法通过 ACC_SYNCHRONIZED 标志位实现。锁升级偏向锁无竞争时Mark Word 记录线程 ID后续该线程进入无需 CAS。轻量级锁出现竞争时线程在栈帧创建 Lock Record通过 CAS 尝试将 Mark Word 指向该记录。失败则自旋等待。重量级锁自旋超过阈值膨胀为重量级锁未获取锁的线程被操作系统挂起阻塞开销最大。AQS 与 ReentrantLock问题 和 有什么区别什么是 AQS参考答案区别 是 JVM 关键字自动释放锁非公平 是 API 层面的锁需在 finally 手动释放支持公平锁、可中断、多 Condition 条件变量。AQS (AbstractQueuedSynchronizer)JUC 并发包的灵魂。核心是一个 变量和一个 CLH 双向阻塞队列。通过模板方法模式子类只需实现 即可实现各种同步器如 ReentrantLock、CountDownLatch。线程池核心原理与线上踩坑问题线程池的核心参数有哪些任务提交后的执行流程是怎样的参考答案七大参数核心线程数、最大线程数、空闲存活时间、时间单位、工作队列、线程工厂、拒绝策略。执行流程新任务提交 - 若线程数 核心线程数创建核心线程 - 若核心线程满放入工作队列 - 若队列满且线程数 最大线程数创建非核心线程 - 若达到最大线程数且队列满执行拒绝策略。拒绝策略AbortPolicy抛异常默认、CallerRunsPolicy调用者线程执行、DiscardPolicy丢弃、DiscardOldestPolicy丢弃最老任务。ThreadLocal 原理与内存泄漏问题ThreadLocal 的实现原理是什么为什么会内存泄漏参考答案原理每个 Thread 内部维护一个 Key 是 ThreadLocal 对象Value 是存储的值。内存泄漏Map 的 KeyThreadLocal是弱引用Value 是强引用。当 ThreadLocal 外部没有强引用时Key 会被 GC 回收但 Value 永远不会被回收。如果线程长期存活如线程池Value 就会一直存在导致内存泄漏。解决方式使用完毕后务必调用 方法。线上故障排查实战问题线上服务 CPU 飙高或发生死锁如何排查参考答案CPU 飙高 找最高 CPU 线程 - 转 16 进制 - 查看堆栈定位代码。也可使用 Arthas 的 直接查看最忙线程。死锁排查使用 查看输出JVM 会自动检测并打印 Found one Java-level deadlock 及互相等待的锁信息。三、 JVM 原理与性能调优JVM 内存模型与垃圾回收问题JVM 内存分哪几块G1 垃圾收集器与 CMS 有什么区别参考答案内存划分堆新生代 Eden/Survivor、老年代、方法区元空间、虚拟机栈、本地方法栈、程序计数器。G1 vs CMSCMS 基于标记-清除会产生内存碎片G1 将堆划分为多个大小相等的 Region逻辑分代但物理不连续采用标记-整理算法避免碎片。G1 通过 RSet 解决跨 Region 引用支持可预测的停顿时间模型MaxGCPauseMillis。类加载机制与双亲委派问题了解 Java 的双亲委派机制吗类加载过程是怎样的参考答案加载过程加载 - 验证 - 准备分配内存并赋零值 - 解析符号引用转直接引用 - 初始化执行 。双亲委派类加载请求先委托父加载器父加载器无法完成时才自己加载。优点保证 Java 核心类库的安全性和唯一性。打破场景JDBC SPI 机制使用线程上下文类加载器、Tomcat 的 Web 容器隔离、OSGi 模块化框架。四、 数据库、事务与 SQL 优化MySQL 索引原理与数据结构问题为什么 MySQL 索引使用 B 树而不是 B 树或 Hash参考答案对比 B 树B 树非叶子节点只存键值和指针不存数据因此单页能容纳更多索引树更矮胖大幅减少磁盘 IO 次数。对比 HashHash 索引仅支持等值查询不支持范围查询和排序。B 树叶子节点通过双向链表连接完美支持范围查询Range Query。事务隔离级别与并发问题问题MySQL 事务的隔离级别有哪些分别解决什么问题参考答案读未提交存在脏读、不可重复读、幻读。读已提交 (RC)解决脏读存在不可重复读、幻读。可重复读 (RR)InnoDB 默认级别。解决脏读、不可重复读。通过 MVCC 间隙锁Gap Lock解决大部分幻读问题。串行化全部解决但并发性能极差。MVCC 与锁机制协同问题MVCC 的实现原理是什么UndoLog 和 RedoLog 的作用和区别参考答案MVCC通过隐藏字段事务 ID、回滚指针 Undo Log 版本链 ReadView读视图实现。快照读普通 Select使用 MVCC 实现非阻塞当前读Update/For Update使用 Next-Key Lock临键锁保证互斥。Undo Log逻辑日志记录数据的反向操作。用于事务回滚和 MVCC 的历史版本链构建。Redo Log物理日志记录“在某个数据页做了什么修改”。用于崩溃恢复Crash-safe采用 WALWrite-Ahead Logging机制将随机 IO 转为顺序 IO。SQL 优化与慢查询排查问题如果出现慢查询如何进行优化索引失效的场景有哪些参考答案排查步骤开启慢查询日志 - 使用 分析执行计划重点看 type 是否为 ALLkey 是否为 NULLExtra 是否有 Using filesort/Using temporary。索引失效场景对索引列进行函数运算或类型隐式转换、违反最左前缀原则、使用 或 、 以 开头、 前后有非索引列。点击下方小卡片免费获取Java面试资料Java面试资料https://mp.weixin.qq.com/s/iBmSycbmeTCSRNKJQ10EuA五、 核心框架 (Spring / Spring Boot / MyBatis)Spring Bean 生命周期与循环依赖问题Spring Bean 的生命周期是怎样的循环依赖怎么解决的参考答案生命周期实例化 - 属性填充 - Aware 接口回调 - BeanPostProcessor 前置处理 - 初始化 - BeanPostProcessor 后置处理 - 销毁。循环依赖通过三级缓存解决。一级缓存存成品 Bean二级缓存存半成品 Bean三级缓存存 ObjectFactory。当 A 依赖 BB 又依赖 A 时B 可以从三级缓存中获取 A 的早期引用若需要 AOP此时会提前触发 AOP 代理从而打破循环。Spring 事务失效场景问题加了 为什么有时候不生效参考答案同类方法调用 绕过了代理对象事务失效。方法非 publicSpring AOP 默认只拦截 public 方法。异常被吞内部 捕获了异常未抛出Spring 无法感知。异常类型错误默认只对 回滚抛出受检异常需配置 。多线程事务上下文绑定在当前线程新线程无法继承事务。Spring Boot 自动装配与 AOP 原理问题Spring Boot 自动配置原理是什么AOP 是如何实现的参考答案自动装配 导入 读取 文件结合 系列注解按需加载配置类。AOP 实现基于动态代理。如果目标对象实现了接口默认使用 JDK 动态代理否则使用 CGLIB 通过生成子类字节码实现代理。MyBatis 防 SQL 注入问题MyBatis 中如何防止 SQL 注入参考答案使用 占位符底层使用 预编译参数会被当作字符串处理有效防止注入。严禁使用 它只是简单的字符串拼接。六、 中间件 (Redis / MQ)Redis 数据类型与 Zset 底层问题Redis 常见的数据结构有哪些Zset 底层怎么实现的参考答案基础类型String、List、Hash、Set、ZSet。Zset 底层当元素较少且长度较短时使用 ZipList压缩列表否则使用 SkipList跳表 Dict字典。字典用于 O(1) 查找分数跳表用于 O(log N) 的范围排序查询。缓存穿透、击穿、雪崩问题这三个问题分别是什么如何解决参考答案穿透查不存在的数据缓存空值短过期、布隆过滤器。击穿热点 Key 过期互斥锁重建缓存、逻辑过期不设置 TTL后台异步更新。雪崩大量 Key 同时过期过期时间加随机值、多级缓存、Redis 高可用集群。Redis 持久化与数据一致性问题Redis 持久化方案有哪些如何保证缓存与数据库双写一致性参考答案持久化RDB快照恢复快但易丢数据、AOF追加日志安全但文件大。生产推荐混合持久化RDB 全量 AOF 增量。双写一致性业界铁律是“先更新数据库再删除缓存”。为防止并发脏数据可采用“延时双删”或基于 BinlogCanal MQ异步删除缓存的最终一致性方案。Kafka 高可用与消息积压问题Kafka 如何保持高可用遇到消息积压怎么处理参考答案高可用依赖 Partition 多副本机制和 ISRIn-Sync Replicas同步列表。Leader 挂掉时从 ISR 中选举新 Leader。消息积压紧急扩容消费者若消费者已达上限新建一个 TopicPartition 数 原数 × 10将积压消息转发过去再启动 10 倍数量的消费者快速消费。七、 实际场景与系统设计问题秒杀系统设计问题设计一个秒杀系统需要考虑哪些问题参考答案核心原则动静分离、请求拦截、异步削峰。技术栈CDN 加速静态资源 - Nginx 限流 - Redis 预扣库存Lua 脚本保证原子性 - 消息队列Kafka/RabbitMQ异步下单 - MySQL 扣减真实库存。防超卖数据库层使用乐观锁。分布式事务问题微服务下如何解决跨库数据一致性TCC 的空回滚和悬挂问题怎么解决参考答案方案选型强一致用 2PC/Seata AT高并发最终一致用 MQ 可靠消息或 TCC。TCC 异常处理空回滚Cancel 接口执行时Try 还没执行。解决Cancel 中检查 Try 是否执行过未执行则直接返回成功。悬挂Cancel 比 Try 先执行。解决Try 执行前检查是否已经执行过 Cancel若执行过则 Try 直接返回不插入业务数据。架构分层与流量控制问题为什么复杂的架构一定要做分层设计滑动窗口限流如何实现参考答案分层设计解耦Controller/Service/Dao、复用、便于独立扩展和测试、隔离故障域。滑动窗口将时间轴划分为多个小格子如 1 秒分 10 个 100ms 的格子。统计时根据当前时间动态计算窗口内格子的请求总和。相比固定窗口解决了边界突刺问题。分布式锁防过期问题分布式锁如何防止业务执行时间过长导致锁提前过期参考答案引入看门狗机制Watchdog。在获取锁成功后启动一个后台定时任务如 Redisson每隔锁过期时间的 1/3如 10 秒自动续期。只要持有锁的客户端不宕机锁就不会过期。由于篇幅有限完整资料点击下方小卡片免费获取Java面试资料https://mp.weixin.qq.com/s/iBmSycbmeTCSRNKJQ10EuA