语法面试题:Java 里那些让人掉坑的细节
有些语法点,面试官问的不是你会不会,而是你理解得有多深。
比如 equals 和 == 的区别,大部分人能说出"equals 比较值,== 比较引用",但接着问:Integer 的 == 为什么有时相等有时不等?很多人就答不上来了。
equals vs ==
Java 里 == 比较的是引用(内存地址),equals 比较的是内容(需要重写):
java
String a = new String("hello");
String b = new String("hello");
a == b; // false,new 了两个不同的对象
a.equals(b); // true,String 重写了 equals,比较内容但注意:String 的 intern() 方法可以把字符串放入常量池:
java
String c = "hello";
String d = "hello";
c == d; // true,都在常量池String、StringBuilder、StringBuffer
| 类 | 线程安全 | 性能 | 适用场景 |
|---|---|---|---|
| String | 安全 | 差(每次创建新对象) | 不可变字符串 |
| StringBuilder | 不安全 | 好 | 单线程字符串拼接 |
| StringBuffer | 安全(synchronized) | 较差 | 多线程字符串拼接 |
final、finally、finalize
| 关键字 | 作用 | 执行时机 |
|---|---|---|
| final | 修饰变量/方法/类,约束不可变 | 编译时 |
| finally | 异常处理块,总会执行 | 异常处理时 |
| finalize | Object 方法,GC 回收前调用 | GC 时(已被废弃) |
值传递
Java 是值传递,但很多人把"值传递"和"引用传递"混淆:
java
// 基本类型:传的是值的副本
int a = 1;
void change(int x) { x = 2; }
change(a);
// a 仍然是 1
// 引用类型:传的是引用的副本(副本还是指向同一个对象)
User u = new User("Tom");
void changeName(User u) { u.setName("Jerry"); }
changeName(u);
// u.getName() 变成 "Jerry",因为副本也指向同一个对象关键理解:Java 传的是"副本",但引用类型的副本指向同一个堆对象。
java
// 下面这个方法无法改变 u 的引用指向
void reassign(User u) { u = new User("Jerry"); } // 改变的是副本,不是原引用内部类
java
// 匿名内部类
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello");
}
};
// Lambda(JDK 8+)
Runnable r2 = () -> System.out.println("Hello");匿名内部类在使用外部变量时,该变量必须是 final 或 effectively final(JDK 8+)。
常见坑点
1. String 不可变
java
String s = "hello";
s = s + "world"; // 创建了新的 String 对象,s 指向新对象
// 原 "hello" 还在内存中,等待 GC2. Integer 缓存
java
Integer a = 127;
Integer b = 127;
a == b; // true,Integer 缓存了 -128 到 127
Integer c = 128;
Integer d = 128;
c == d; // false,超出缓存范围
c.equals(d); // true3. BigDecimal 的坑
java
BigDecimal a = new BigDecimal("0.1"); // ✅ 用字符串构造
BigDecimal b = new BigDecimal(0.1); // ❌ 用 double,会有精度问题
a.equals(b); // false!总结
Java 语法看似简单,但每个知识点背后都有值得深挖的东西。面试时不仅要知道"是什么",更要理解"为什么这样设计"。
