Skip to content

HashMap


第一轮

🤖 AI面试官

说一下 HashMap 的底层实现原理?JDK1.7 和 1.8 有什么区别?为什么要做这些改动?

👤 本人回答

HashMap的底层是数组 具体而言是结合了链表+红黑树实现的,对于jdk1.7和1.8 主要的区别就是1.8引入了红黑树,这样的话对于他的解决冲突和扩容进行一些优化

🧐复盘:

👉 优点: 知道“数组 + 链表 + 红黑树”

知道 1.8 引入红黑树

👉 问题(重点):

❌ 没讲清结构(数组里到底存什么?)

❌ 没讲冲突怎么解决(只说“优化”太空)

❌ 完全没提 hash、index计算

❌ 没讲 1.7 vs 1.8 核心差异(头插 vs 尾插)

❌ 没讲 resize

❌ 没讲 树化条件

❌ “为什么优化”没说出来(这是加分点)


第二轮

🤖 AI面试官

HashMap 是怎么根据 key 找到数组下标的?hash 是怎么处理的?

👤 本人回答

先计算key的hashCode,然后通过(table.length-1)&hash 计算位置,对于hash 是怎么处理的 我不太理解这个问题

🧐复盘:

👉 你说对的部分:

✔️ hashCode() ✔️

✔️ (n - 1) & hash ✔️(这个很关键!不少人不会)

👉 不足的地方:

❌ 没说 为什么要 & 运算(而不是 %)

❌ 没说 hash 是怎么“处理”的(扰动函数)

❌ 没说 为什么要减少哈希冲突


第三轮

🤖 AI面试官

什么时候链表会变成红黑树?为什么是这个阈值?

👤 本人回答

链表长度大于8并且数组长度大于等于64

🧐复盘:

👉 你现在的回答:

链表长度 > 8 且数组长度 ≥ 64

👉 问题在于:

只报结论,没有解释

❌ 没说“为什么是8”

❌ 没说“为什么要加64这个限制”

❌ 没说“如果不满足会发生什么”


总结

HashMap 底层是基于数组实现的哈希表结构,每个数组位置是一个桶,当发生哈希冲突时会使用链表或红黑树来存储数据。在插入元素时,首先通过 key 的 hashCode 计算出 hash 值,并通过扰动函数让高位参与运算以减少冲突,然后通过 (n - 1) & hash 计算数组下标,因为数组长度是 2 的幂,这样可以用位运算代替取模提升性能。如果该位置没有元素就直接插入,否则会判断 key 是否相同,相同则覆盖,不同则插入到链表中,在 JDK1.8 中当链表长度超过 8 且数组容量大于等于 64 时会转为红黑树以提升查询性能。在扩容方面,当元素数量超过容量乘以负载因子(默认0.75)时会进行 resize,扩容为原来的两倍。JDK1.7 和 1.8 的主要区别在于 1.8 引入了红黑树,并且将链表插入方式由头插法改为尾插法,从而避免了在多线程扩容时可能出现的链表成环死循环问题,不过 HashMap 本身仍然是非线程安全的,在并发场景下通常会使用 ConcurrentHashMap 来替代。

synchronizedReentrantLock


第一轮

🤖 AI面试官

synchronizedReentrantLock 有什么区别?你在项目中怎么选择?

👤 本人回答

synchronized 和 ReentrantLock都是用来进行加锁的 synchronized用于比较简单的业务 是隐式锁ReentrantLock用于比较复杂的业务是显式锁

🧐复盘:

👉优点: 知道都是锁

知道一个隐式,一个显式(不错的切入点)

👉 主要问题(比较关键):

太抽象,没有技术细节

什么叫“复杂”?面试官不会认可这种说法

没有对比核心能力(重点缺失)

可中断 ❌

公平锁 ❌

tryLock ❌

Condition ❌

没有“项目场景”

你说“复杂业务”,但没举例 → 等于没说

没有结论导向


第二轮

🤖 AI面试官

ReentrantLock 相比 synchronized,多了哪些能力?

👤 本人回答

支持公平锁和非公平锁、支持自定义中断、可以自行释放

🧐复盘:

👉 优点:

✔️ 提到了公平锁 / 非公平锁(这是关键点)

✔️ 知道“可中断”这个能力(方向是对的)

✔️ 知道需要“手动释放锁”(显式锁的特点)

👉 缺点:

❌ “自定义中断”表述不准确 → 应该说:支持可中断获取锁(lockInterruptibly)

❌ “自行释放”不算优势点 → 这是特性,但不是“比 synchronized 强”的能力

❌ 漏掉两个高频加分点: tryLock()(非阻塞获取锁) Condition(替代 wait/notify,更灵活)

❌ 没有解释“这些能力解决什么问题” → 面试官更想听的是:为什么要这些功能?


第三轮

🤖 AI面试官

你刚提到公平锁,那公平锁和非公平锁有什么区别?为什么默认用非公平锁?

👤 本人回答

公平锁按照线程等待的先后顺序获取锁,非公平锁允许插队,为了节省开销 所以默认使用公平锁

🧐复盘:

👉 优点:

  1. ✔️ 说清了公平锁是“按等待顺序获取锁”

  2. ✔️ 知道非公平锁允许“插队”

  3. ✔️ 意识到和“性能/开销”有关(方向是对的)

👉 缺点:

  1. 结论说反了(这是关键错误) → 默认是:非公平锁,不是公平锁

  2. ❌ “节省开销”说得太模糊 → 面试官更想听:为什么非公平锁吞吐量更高

  3. ❌ 没说公平锁的缺点 → 上下文不完整(像只记了一半)


第四轮

🤖 AI面试官

在你的项目中,什么时候会用 synchronized,什么时候会用 ReentrantLock?请举一个具体场景。

👤 本人回答

比如基础信息修改等不怎么频繁 条件比较少的场景下 使用synchronized,比如订单核销等条件比较多的情况下使用ReentrantLock

🧐复盘:

👉 优点:

  1. ✔️ 知道要用“业务场景”来区分(方向是对的)
  2. ✔️ 能举出例子(基础信息修改 / 订单核销),比纯概念强
  3. ✔️ 有“复杂度不同选不同锁”的意识

👉 缺点(这一题的关键问题):

  1. ❌ “条件多/少”这个标准不准确
    → 锁的选择本质不是看业务复杂度,而是看并发控制需求
  2. ❌ 场景不具体
    → “订单核销条件多”这句话没有并发细节(面试官会觉得你在泛说)
  3. ❌ 没说出 ReentrantLock 真正用武之地:
    • tryLock(防止阻塞)
    • 可中断(避免死等)
    • 多条件队列(Condition)
  4. ❌ 没体现“为什么不能用 synchronized”
    → 这是拉开差距的关键点

总结

synchronized 和 ReentrantLock 都是实现线程同步的方式,synchronized 是 JVM 层面的内置锁,使用简单,会自动释放锁,而 ReentrantLock 是基于 AQS 实现的显式锁,需要手动加锁和释放。相比之下,ReentrantLock 提供了更多灵活的功能,比如支持公平锁和非公平锁、支持可中断获取锁、支持 tryLock 尝试获取锁避免阻塞,以及通过 Condition 实现更灵活的线程通信。在实际项目中,如果是简单的同步场景或者并发不高的情况,我一般会使用 synchronized;而在需要更高并发控制能力的场景下,比如需要避免线程长时间阻塞、支持中断或者实现复杂的线程调度时,会选择 ReentrantLock,比如在订单处理或库存扣减中,可以通过 tryLock 实现快速失败或降级处理,提高系统的可用性。

Spring Bean


第一轮

🤖 AI面试官

Spring Bean 的生命周期是怎样的?

👤 本人回答

实例化 → 属性填充 → 初始化 → 销毁

🧐复盘:

👉 优点:

  1. ✔️ 知道核心四步(实例化 → 填充 → 初始化 → 销毁)
  2. ✔️ 没有说错方向(这一点很重要)

👉 缺点:

  1. ❌ 过于“骨架化”,缺少关键细节(面试官会觉得你只背了标题)
  2. ❌ 没提 Spring 的核心扩展点(这一题最重要的部分)
  3. ❌ 没提:
    • Aware 接口
    • BeanPostProcessor
    • init-method / destroy-method
  4. ❌ 没体现“Spring 为什么强”(IOC 扩展能力)

第二轮

🤖 AI面试官

在 Bean 初始化前后,Spring 是如何让开发者“介入”的?

👤 本人回答

通过BeanPostProcessor拦截所有 Bean 的初始化前后,再配合@PostConstruct、InitializingBean等单 Bean 初始化接口,实现开发者自定义介入。

🧐复盘:

👉 优点:

  1. ✔️ 提到了 BeanPostProcessor(这一题核心命门)
  2. ✔️ 知道“初始化前后拦截”(说明理解流程,而不是只背步骤)
  3. ✔️ 能补充 @PostConstructInitializingBean(有扩展意识)

👉 缺点:

  1. ❌ 表达有一点“混层级”
    → BeanPostProcessor(全局扩展) vs InitializingBean(单 Bean 生命周期),最好区分清楚
  2. ❌ 少了两个关键方法名(面试加分点):
    • postProcessBeforeInitialization
    • postProcessAfterInitialization
  3. ❌ 没点出一个核心价值:
    👉 AOP、代理对象就是在这里产生的(非常加分)
  4. ❌ 没提 Aware 接口(常见追问点)

第四轮

🤖 AI面试官

AOP 代理对象是在 Bean 生命周期的哪个阶段生成的?为什么是在这个阶段?

👤 本人回答

Bean 初始化完成之后,不知道为什么

🧐复盘:

👉 优点:

  1. ✔️ 知道是在初始化之后(关键结论是对的)
  2. ✔️ 没乱编(这一点比很多人强)

👉 缺点:

  1. ❌ 不知道“为什么”(这一题真正的考点)
  2. ❌ 没说具体是在 哪个扩展点 做的(BeanPostProcessor)
  3. ❌ 没理解一个核心点:
    👉 代理对象必须“包裹一个完整的原始对象”

总结

Spring Bean 的生命周期主要包括实例化、属性填充、初始化和销毁几个阶段。在实例化之后会进行依赖注入,然后如果实现了 Aware 接口会注入相关资源;在初始化阶段前后,Spring 通过 BeanPostProcessor 提供了两个扩展点,可以对 Bean 进行增强处理,比如 AOP 代理对象就是在 postProcessAfterInitialization 阶段生成的;此外,开发者也可以通过 @PostConstruct、InitializingBean 或 init-method 来自定义初始化逻辑,最后在容器关闭时执行销毁方法。

更新: 5/4/2026 字数: 0 字 时长: 0 分钟

第一轮

🤖 AI面试官

👤 本人回答

🧐复盘:


第一轮

🤖 AI面试官

👤 本人回答

🧐复盘:


第一轮

🤖 AI面试官

👤 本人回答

🧐复盘:

最后更新于:

Released under the MIT License.