25年12月天阳科技Java后端开发工程师 一面
1. String a="test" 和 new String("test"),a==b 和 a.equals(b) 结果?
思路
核心区分“字符串常量池”和“堆对象”,讲清==(地址)和equals(内容)的本质。
回答示例
面试官您好,结果分两种:
a == b:结果是false。因为a指向字符串常量池里的"test"对象,new String("test")会在堆里新建一个对象,b指向堆对象,两者地址不同;a.equals(b):结果是true。因为String的equals重写过,比较的是字符串内容,两者内容都是"test"。
补充:如果执行b = b.intern(),再比较a==b就会是true,因为intern()会把堆字符串引用指向常量池。
2. final、finally、finalize 三者区别?
思路
从“用途、归属、执行时机”三个维度区分,避免混淆。
回答示例
三者完全不同,核心区别:
- final:关键字,修饰类(不能被继承)、方法(不能被重写)、变量(赋值后不可改);
- finally:异常处理关键字,try-catch-finally块里的部分,除非JVM崩溃(如System.exit(0)),否则必执行,常用于释放资源;
- finalize:Object类的方法,GC回收对象前会调用(不保证执行时机),用于清理非堆资源(如文件句柄),已被标记为过时。
3. ArrayList 与 LinkedList 底层结构及适用场景?
思路
对比底层结构,结合“增删改查”效率说场景,直观易懂。
回答示例
底层结构
- ArrayList:动态数组,底层是Object[]数组,扩容时复制新数组;
- LinkedList:双向链表,每个节点存数据+前后节点引用。
适用场景
- ArrayList:适合随机访问(get/set)多的场景(如按索引查数据),数组下标访问效率O(1);
- LinkedList:适合频繁增删(非首尾) 的场景(如中间插入/删除),链表修改只需改引用,效率O(1);
注意:LinkedList首尾增删也高效,但随机访问效率O(n),远不如ArrayList。
4. HashMap 扩容机制?JDK1.8 为何链表超8转红黑树?
思路
先讲扩容核心规则,再解释红黑树的性能考量(统计规律+效率平衡)。
回答示例
扩容机制(JDK1.8)
- 初始容量16,负载因子0.75,当元素数≥容量×负载因子(如16×0.75=12),触发扩容;
- 扩容后容量翻倍(16→32),重新计算每个元素的哈希槽位,JDK1.8做了优化,槽位要么留在原位置,要么移到“原位置+旧容量”位置;
- 扩容时如果链表长度≤6,红黑树会转回链表。
链表超8转红黑树的原因
- 哈希冲突的链表长度遵循“泊松分布”,长度超过8的概率极低(约0.00000006);
- 链表查询效率O(n),红黑树查询O(logn),当链表过长时,转红黑树提升查询性能;
- 阈值设8而非更小,是避免频繁树化/退化(树化有额外内存开销),平衡性能和内存。
5. ConcurrentHashMap JDK1.7 与 1.8 实现差异?
思路
从“锁机制、数据结构、并发度”三个核心维度对比,突出1.8的优化。
回答示例
维度 |
JDK1.7 |
JDK1.8 |
锁机制 |
分段锁(Segment),每个Segment是ReentrantLock |
CAS + synchronized,锁粒度降到单个哈希桶 |
数据结构 |
Segment数组 + 哈希桶数组 + 链表 |
哈希桶数组 + 链表/红黑树 |
并发度 |
取决于Segment数量(默认16),最多16线程并发 |
理论上无上限,只要访问不同桶就无锁竞争 |
扩容 |
分段扩容,每个Segment独立扩容 |
整体扩容,迁移时用CAS+自旋 |
核心优化:1.8去掉分段锁,锁粒度更小,并发效率更高,还引入红黑树提升查询性能。
6. 线程创建方式?Runnable 与 Callable 区别?
思路
先讲线程创建的3种方式,再聚焦Runnable和Callable的核心差异(返回值、异常)。
回答示例
线程创建方式(3
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
本专栏在精不在多,内容分为八股文、大厂真实面经,面试通过后将offer和面试题私发给我,可退还专栏的收益部分费用。欢迎大家共建专栏

查看15道真题和解析