面试现场:大厂Java岗初试
面试官(推了推眼镜):请做个自我介绍。
谢飞机:我叫谢飞机,擅长Ctrl+C和Ctrl+V,GitHub star过10(自己点的)。
面试官(面无表情):第一个问题,HashMap的底层结构是什么?
谢飞机:数组+链表!JDK8后链表长度超8转红黑树!
面试官(点头):不错,那扩容机制呢?
谢飞机:默认负载因子0.75,容量翻倍,然后rehash!
面试官:如果两个key的hashCode相同但equals不同,怎么处理?
谢飞机:……那个,我用的是TreeMap!
面试官(皱眉):ArrayList和LinkedList的区别?
谢飞机:一个像数组,一个像链表!查询用前者,增删用后者!
面试官:那modCount是干嘛的?
谢飞机:防并发修改!不过我一般都用synchronizedList!
面试官:Spring Bean的作用域有哪些?
谢飞机:singleton、prototype,还有request、session……反正我只用单例!
面试官:Bean的生命周期?
谢飞机:实例化、填充属性、初始化……销毁!中间还能加Aware接口!
面试官:AOP原理?
谢飞机:动态代理!JDK代理和CGLIB!
面试官:ThreadLocal有了解吗?
谢飞机:线程隔离!我用来存用户信息!
面试官:内存泄漏怎么防?
谢飞机:……我重启服务!
面试官:Redis持久化机制?
谢飞机:RDB快照,AOF日志追加!
面试官:缓存穿透怎么办?
谢飞机:布隆过滤器!空值缓存!
面试官:MySQL索引失效场景?
谢飞机:左模糊、函数操作、类型转换……
面试官:explain的type字段有哪些?
谢飞机:system、const、ref、range、index、all!
面试官:DDD的聚合根是什么?
谢飞机:就是……一个根?我们项目没用DDD,用的是Copy Copy Design!
面试官(扶额):你先回去等通知吧。
谢飞机:好嘞,我回去准备offer庆祝宴!
参考答案详解
1. HashMap 底层结构与扩容
- JDK8前:数组 + 链表(解决哈希冲突)
- JDK8后:当链表长度 ≥ 8 且数组长度 ≥ 64 时,转为红黑树,提升查找性能至 O(logN)
- 扩容:触发条件为元素数量 > 容量 × 负载因子(默认0.75),扩容为原容量2倍,需 rehash
- 注意:多线程下扩容可能引发死循环(JDK7),JDK8已修复
2. ArrayList vs LinkedList
- ArrayList:基于动态数组,支持随机访问 O(1),插入删除慢 O(N)
- LinkedList:双向链表,插入删除快 O(1),访问慢 O(N)
- modCount:用于快速失败机制(fail-fast),遍历时若被其他线程修改则抛 ConcurrentModificationException
3. Spring Bean 生命周期
- 实例化(new)
- 属性赋值(DI)
- Aware 接口回调(如 BeanNameAware)
- BeanPostProcessor 前置处理
- 初始化方法(@PostConstruct / init-method)
- BeanPostProcessor 后置处理
- 使用
- 销毁(@PreDestroy / destroy-method)
4. AOP 实现原理
- JDK 动态代理:基于接口,使用 Proxy 和 InvocationHandler
- CGLIB 动态代理:基于子类,生成目标类的子类并重写方法
- Spring 默认优先使用 JDK 代理,若无接口则使用 CGLIB
5. ThreadLocal 内存泄漏
- 每个线程持有 ThreadLocalMap,Key 为弱引用,Value 为强引用
- 若不调用 remove(),线程长期运行时 Value 不会被回收,导致内存泄漏
- 正确做法:使用 try-finally 调用 remove()
6. Redis 持久化
- RDB:定时快照,恢复快,可能丢数据
- AOF:记录写命令,日志追加,可配置 fsync 策略(每秒/每次)
- 混合模式(Redis 4.0+):AOF 重写时包含 RDB 格式数据
7. 缓存穿透
- 定义:查询不存在的数据,绕过缓存直击数据库
- 解决方案:
- 布隆过滤器判断 key 是否存在
- 空值缓存,并设置短过期时间
- 接口层限流 + 参数校验
8. MySQL 索引失效
- 最左前缀原则破坏(跳过联合索引第一列)
- 使用函数或表达式(如 WHERE YEAR(create_time) = 2024)
- 类型隐式转换(字符串字段传数字)
- 使用 OR 连接非索引字段
- 左模糊查询(LIKE '%abc')
9. explain type 字段
- system > const > eq_ref > ref > range > index > ALL
- 最佳为 const/system(主键或唯一索引等值查询)
- range 表示范围扫描,index 为全索引扫描,ALL 为全表扫描(需优化)
10. DDD 聚合根(Aggregate Root)
- 聚合:一组相关对象的集合,作为一个数据变更单元
- 聚合根:聚合的根实体,外部只能通过聚合根访问内部对象
- 保证事务一致性边界,避免脏读、并发冲突
- 示例:订单 Order 是聚合根,其下的订单项 OrderItem 不能单独修改