简介:本文系统梳理 Java 面试中的高频核心问题,覆盖基本数据类型与包装类、String 三剑客、equals 与 hashCode 约定、缓存与拆装箱陷阱、深浅拷贝、注解机制及异常体系等关键考点,逐条拆解底层原理与答题思路,帮助你把零散知识点串成完整知识链,从容应对技术面。
在 Java 工程师的招聘流程中,基础语法的深度考察往往是技术面的第一道关卡。面试官看似在问“背概念”式的问题,实际是在检验候选人对底层原理的理解程度与知识体系的完整性。本文围绕十余个高频问题逐条展开,帮助开发者做到“知其然,更知其所以然”。
Java 提供了 8 种基本数据类型,其占用空间与对应包装类型如下:
| 基本类型 | 占用字节 | 包装类型 |
|---|---|---|
| byte | 1 | Byte |
| short | 2 | Short |
| int | 4 | Integer |
| long | 8 | Long |
| float | 4 | Float |
| double | 8 | Double |
| char | 2 | Character |
| boolean | 规范未明确定义 | Boolean |
boolean 比较特殊:JVM 规范并未规定其确切大小,具体取决于虚拟机实现,单个变量通常按 int 处理,数组场景下可能按字节编码。回答时主动提及这一点,能体现对规范与实现差异的理解。
包装类型的存在是为了让基本类型获得面向对象能力:泛型集合要求元素为引用类型、需要缓存机制与工具方法等场景都离不开它。同时也埋下了两个隐患——空指针风险与 == 比较陷阱,后文详述。
三者都用于处理字符序列,核心差异在于可变性与线程安全性:
选型建议:常量或少量拼接用 String;循环内大量拼接用 StringBuilder;仅在多线程共享同一缓冲区时才考虑 StringBuffer。补充加分点:编译器会把 String 的 + 拼接优化为 StringBuilder 的 append 调用,但若拼接发生在循环体内,每次迭代都会新建一个 StringBuilder,仍会带来额外对象开销。
以 JDK 8 为例,String 内部维护一个 private final char[](JDK 9 后改为 byte[] 以节省内存),类本身被 final 修饰无法继承,且对外不暴露任何修改数组的入口。这种设计带来四大收益:
经典答案是“最多两个”。分两个层面理解:
new 关键字则必然在堆中再创建一个全新的 String 实例。因此:池中已有 “abc” 时只创建 1 个对象,否则创建 2 个。这道题真正考察的是常量池机制与对象创建时机,而不是背一个数字。
==:对基本类型比较值;对引用类型比较内存地址;equals:Object 的默认实现就是 ==;String、Integer 等类将其重写为内容比较。必须掌握的约定是:重写 equals 必须同时重写 hashCode。因为 HashMap、HashSet 等容器先依据 hashCode 定位桶,再用 equals 精确判等。若两个对象 equals 相等而 hashCode 不同,它们会被分配到不同桶中,导致 HashSet 去重失效、HashMap 取不到已放入的值。反过来,hashCode 相等不代表对象相等——哈希冲突客观存在。
为避免小数值对象的重复创建,包装类在类加载时预先构建缓存池:
由此产生经典陷阱:
Integer a = 127, b = 127;System.out.println(a == b); // true,命中缓存,实为同一对象Integer c = 128, d = 128;System.out.println(c == d); // false,缓存之外各自新建System.out.println(c.equals(d)); // true,内容比较
结论:包装类型之间的判等必须使用 equals,绝不能使用 ==。
装箱指基本类型转包装类型,拆箱反之,二者本质是编译期语法糖:装箱调用 valueOf(),拆箱调用 xxxValue()。使用时警惕两类风险:
三者层层递进,务必区分清楚:
clone();判断口诀:修改新对象的内层属性不影响原对象,才是真正的深拷贝。实际开发中当对象关系复杂时,序列化方案虽有一定开销,但实现简单且不易遗漏字段。
注解是附加在代码上的元数据,本身不含业务逻辑,必须配合编译器、字节码处理工具或反射才能发挥作用。它主要解决三类问题:
四个元注解需要掌握:@Retention 指定保留策略(SOURCE、CLASS、RUNTIME),@Target 指定可标注的位置,@Inherited 允许被子类继承,@Documented 使注解纳入 API 文档。只有 RUNTIME 级别的注解才能在运行时被反射读取,这是自定义注解时最容易踩的坑。
二者都继承自 Throwable,但定位截然不同:
回答时最好补充工程实践:不要捕获异常后什么都不做(吞异常会让排障极其困难),不要用异常控制正常业务流程;关闭资源优先使用 try-with-resources;finally 中避免写 return,以免覆盖真实返回值。
这些高频问题并非孤立考点,而是一棵知识树:从数据类型到对象模型(包装类、拆装箱),从内存布局到常量池机制(String、对象创建、== 与 equals),从对象复制到设计思想(深浅拷贝、注解),再到健壮性设计(异常体系)。面试时应先给出准确结论,再解释底层原理,最后补充工程实践中的取舍,这样的回答层次最能打动面试官。把每一个“为什么”都追问到底,才能真正构建起经得起追问的知识体系。