final、finally、finalize 是 Java 基础面试必考「三兄弟」,虽名似但职责截然不同。本文从语法、语义、字节码、实战四个维度彻底澄清。
| 修饰目标 | 语义 | 典型场景 |
|---|---|---|
| 类 | 不可被继承 | String、Wrapper 类、工具类 |
| 方法 | 不可被重写 | 模板方法模式固定骨架、安全敏感操作 |
| 变量(基本类型) | 值不可变 | 常量定义 final double PI = 3.14 |
| 变量(引用类型) | 引用不可变(指向对象不可变),但对象内部状态可变 | final List<String> list = new ArrayList<>(); list.add("ok"); // 合法 |
编译期常量折叠:final 修饰的基本类型/String 编译期确值,直接内联到调用处字节码,性能微增。
try (Resource res = acquire()) { // Java 7+ try-with-resources 自动 close
use(res);
} catch (Exception e) {
handle(e);
} finally {
// 无论正常/异常/return 都会执行
cleanup();
}
return/break/continue 前System.exit(0) 终止 JVMThread.stop() 强制终止(已废弃)public int test() {
try { return 1; }
finally { return 2; } // 方法返回 2,原返回值/异常被吞没!
}
禁忌:finally 块中严禁使用 return/throw,否则异常丢失、调试极难。
@Deprecated(since="9")
protected void finalize() throws Throwable { ... }
AutoCloseable,编译器自动生成 finally close 代码Cleaner.create(obj, cleaningAction),基于幻象引用,可控、可取消、线程安全// 1. 传统模式(易泄漏)
Socket s = new Socket();
try { use(s); }
catch (IOException e) { s.close(); throw e; }
s.close(); // 重复代码,异常路径易漏
// 2. finally 兜底(冗长)
Socket s = new Socket();
try { use(s); }
finally {
if (s != null) try { s.close(); } catch (IOException ignored) {}
}
// 3. try-with-resources (推荐)
try (Socket s = new Socket()) {
use(s);
} // 编译器自动生成 null 检查 + 异常抑制的 close()
// 4. Cleaner (库/框架级)
Cleaner cleaner = Cleaner.create();
Socket s = new Socket();
cleaner.register(s, () -> { try { s.close(); } catch (IOException e) { log.error(e); } });
// 业务代码无感,Cleaner 统一管理
final 定约不可变性,finally 兜底资源释放,finalize 是历史包袱已被 AutoCloseable/Cleaner 取代。掌握 try-with-resources 与 Cleaner,写出不泄漏、可维护的资源管理代码,是现代 Java 程序员必备基本功。