final、finally、finalize 的区别?

#Java#面试八股

final、finally、finalize 的区别?

结论:三个词只是长得像,语义毫无关联。final 是修饰符,表示"不可再变":类不可被继承、方法不可被重写、变量不可被重新赋值。finally 是异常处理语句块,配合 try 使用,用来放必须执行的收尾逻辑(关闭资源),通常一定会执行。finalize() 是 Object 的方法,GC 回收对象前的一次回调;它已被标记废弃(deprecated),JDK 18 起 finalization 机制被标记为"将被移除"(JEP 421),新代码不应使用。

1. 三者对照

关键字类别作用对象语义现状
final修饰符类、方法、变量、参数不可继承 / 不可重写 / 不可重新赋值常用
finally语句块try 结构收尾代码,正常与异常路径都执行常用,资源关闭优先用 try-with-resources
finalize方法对象GC 前回调,做最后清理废弃,禁止使用

2. final 的三种用法

final class Config { }                  // ① 类:不可被继承

class Base {
    final void hook() { }               // ② 方法:子类不可重写
}

class Demo {
    static final int MAX = 100;         // ③ 变量:不可二次赋值
    final List<String> list = new ArrayList<>();

    void add() {
        list.add("a");                  // 合法:final 约束的是"引用不能再指向别人"
        // list = new ArrayList<>();    // 编译错误
    }
}

三个容易答错的点:

  1. final 引用 ≠ 对象不可变。final List 仍可 add,只是不能重新赋值;要真正不可变还得配合 Collections.unmodifiableList 或 List.of;
  2. static final 常量会被内联。编译期常量(如 static final int、static final String)在调用方字节码里直接写入字面量,改常量后依赖方要重新编译才能生效,否则仍用旧值;
  3. final 参数只在方法体内生效,且 Java 的匿名类/lambda 捕获要求变量"有效 final(effectively final)",不必显式加 final。

3. finally 的典型写法

public void query() {
    Connection conn = null;
    try {
        conn = dataSource.getConnection();
        // ... 业务
    } catch (SQLException e) {
        log.warn("query failed", e);
    } finally {
        closeQuietly(conn);             // 无论成功、失败、提前 return 都会走到
    }
}

// 更推荐:try-with-resources 由编译器生成 close 调用与异常抑制
try (Connection conn = dataSource.getConnection();
     PreparedStatement ps = conn.prepareStatement(SQL)) {
    ps.executeQuery();
}

try-with-resources 本质也是 finally,但会把 close() 抛出的异常作为 suppressed(抑制异常) 挂到主异常上,不会像手写 finally 那样把业务异常吞掉。

4. finalize 为什么被废弃

问题说明
执行时机不确定由 GC 触发,可能几分钟后才执行,也可能整个进程都不执行
拖慢 GC带 finalize 的对象至少要经历两轮回收,还进入 F-Queue 由低优先级线程处理
可"复活"对象finalize 里把 this 赋给静态字段,对象复活,破坏回收语义
异常被忽略finalize 抛出的异常会被丢弃,问题难发现
替代方案成熟try-with-resources、java.lang.ref.Cleaner(JDK 9+)
// 反例:用 finalize 释放文件句柄
@Override
@Deprecated(since = "9", forRemoval = true)
protected void finalize() throws Throwable {
    file.close();      // 可能永远不执行;异常会被吞掉
    super.finalize();
}

5. 常见追问

追问回答要点
final 能修饰构造器吗?不能,构造器不能被继承或重写,没有意义
final 方法能被重载吗?能,重载(overload)是新方法,与是否 final 无关
finally 和 finalize 有什么关系?没关系,只是名字相似
什么时候用 final?常量、不可变字段、模板方法中不允许子类改写的钩子、参数防误改
为什么单例常用 static final?类初始化时创建,JVM 保证线程安全且只执行一次
Cleaner 比 finalize 好在哪?独立清理线程、可注册多个清理动作、不依赖对象复活,但仍不保证及时执行

再补一个面试常问的对比:finally 里的代码"通常"会执行,但并不绝对——System.exit()、JVM 崩溃、守护线程退出都会跳过 finally,这部分在下一篇展开。

小结

final 管"不可变",finally 管"必执行",finalize 管"回收前回调"且已被放弃。写代码时:字段能 final 就 final,资源关闭用 try-with-resources,任何场景都不要重写 finalize。

笔记加载中…