Skip to content

语法面试题: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异常处理块,总会执行异常处理时
finalizeObject 方法,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" 还在内存中,等待 GC

2. 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);      // true

3. BigDecimal 的坑

java
BigDecimal a = new BigDecimal("0.1");  // ✅ 用字符串构造
BigDecimal b = new BigDecimal(0.1);  // ❌ 用 double,会有精度问题
a.equals(b);     // false!

总结

Java 语法看似简单,但每个知识点背后都有值得深挖的东西。面试时不仅要知道"是什么",更要理解"为什么这样设计"。

基于 VitePress 构建