final、finally、finalize 的区别?
final、finally、finalize 的区别?
zhangchangsheng面试考察点
- 基础知识全面性:面试官不仅仅是想知道你能否区分这三个长得像的关键词,更是想考察你对 Java 基础知识的掌握是否全面——从修饰符、异常处理到对象生命周期。
- 实践应用能力:考察你是否在实际项目中正确使用过
final(如不可变类、常量定义),是否理解finally在资源释放中的作用,以及是否知道finalize()已被废弃。 - 技术敏感度:看你是否关注 Java 版本演进,知道
finalize()从 JDK 9 开始被标记为@Deprecated,理解其被废弃的原因。
核心答案
这三个词虽然名字相似,但 完全不是一类东西: ****
| 关键词 | 类型 | 作用 | 使用场景 |
|---|---|---|---|
| final | 关键字 | 修饰符,表示 “不可变” | 类、方法、变量 |
| finally | 关键字 | 异常处理代码块 | 与 try-catch 配合,确保代码执行 |
| finalize() | 方法 | 对象回收前的回调 | 已废弃,不推荐使用 |
一句话概括:final 修饰不可变,finally 确保必执行,finalize() 回收前调用(已废弃)。
深度解析
一、final:不可变修饰符
final 是 Java 中的修饰符,可以修饰 类、方法、变量,表示 “最终的、不可改变的”。
| 修饰对象 | 示例 | 含义 | 典型应用 / 注意点 |
|---|---|---|---|
| 修饰类 | final class String { ... } |
类不能被继承 | String、Integer 等不可变类 |
| 修饰方法 | public final void method() { ... } |
方法不能被重写 | 模板方法模式中防止子类修改 |
| 修饰变量 | final int MAX_SIZE = 100;、final User user = new User(); |
变量只能赋值一次(常量) | 引用类型是引用不可变,对象内容仍可变 |
上图展示了 final 的三种使用场景,下面详细说明:
- 修饰类:该类不能被继承,所有成员方法隐式变为
final(无法重写)。典型应用是 Java 的包装类String、Integer等,保证不可变性从而实现线程安全和字符串常量池优化。 - 修饰方法:该方法不能被子类重写,但可以重载。常用于模板方法模式中,固定算法骨架不允许子类修改。
- 修饰变量:变量只能赋值一次,成为常量。对于基本类型,值不可变;对于引用类型,引用不可变但对象内容可以改变。
// final 修饰类:不能继承 |
final 的常见面试陷阱:
public class FinalTrap { |
二、finally:异常处理的保障机制
finally 是异常处理机制中的关键字,与 try-catch 配合使用,无论是否发生异常,finally 块中的代码都会执行。
flowchart TB
T["try 执行业务代码"] --> C1{"是否抛出异常?"}
C1 --> |"否"| OK["正常执行完毕"]
C1 --> |"是"| C2{"匹配到 catch 块?"}
C2 --> |"是"| CAT["执行 catch 块"]
C2 --> |"否"| UP["异常向上抛出"]
OK --> F["finally 块必执行"]
CAT --> F
UP --> F
上图展示了 finally 的执行时机,关键点在于:
- 无论是否发生异常:
finally块都会执行 - 即使
try或catch中有return:finally也会在return之前执行 - 只有一种情况不执行:在
try或catch中调用了System.exit()导致 JVM 退出
// finally 的典型使用场景:资源释放 |
finally 的经典面试题:
public class FinallyReturn { |
重要规则:如果 finally 中有 return 语句,会覆盖 try 或 catch 中的 return 值。但实际开发中 强烈不建议 在 finally 中使用 return。
三、finalize():已废弃的对象回收回调
finalize() 是 Object 类中的一个方法,在对象被垃圾回收器回收之前调用。但从 JDK 9 开始已被标记为 @Deprecated(forRemoval=true),计划在未来移除。
flowchart TB
A["创建对象"] --> B["正常使用"] --> C["变为垃圾(不可达)"]
C --> D["GC 发现对象可被回收"]
D --> E{"对象是否重写 finalize()?"}
E --> |"否"| F["直接回收对象内存"]
E --> |"是"| G["放入 F-Queue,稍后自动调用 finalize()"]
G --> H["执行 finalize():对象可在其中重新引用实现自救"]
W["注意:finalize() 执行时间不确定,甚至可能不执行"]
上图展示了 finalize() 的执行时机,但这个机制存在严重问题:
- 执行时间不确定:取决于 GC 的运行,可能很长时间都不执行
- 可能根本不执行:程序结束时 GC 可能还没运行
- 性能开销大:带有
finalize()的对象需要特殊处理,影响 GC 效率 - 可能导致内存泄漏:如果在
finalize()中让对象重新被引用,会导致对象无法被回收 - 不安全:
finalize()可能被恶意代码利用
// finalize() 的使用(不推荐!) |
四、三者对比总结
| 关键字 | 所属层面 | 作用 | 常见用途 | 推荐度 |
|---|---|---|---|---|
| final | 修饰符 | 修饰类 → 不可继承;修饰方法 → 不可重写;修饰变量 → 不可重新赋值 | 定义常量、工具类 | 常用,推荐使用 |
| finally | 异常处理块 | try-catch-finally 结构,确保代码一定执行 | 资源释放 | 常用,推荐使用 |
| finalize() | Object 类的方法 | GC 回收对象前调用 | 清理资源 | JDK 9 已废弃,不推荐使用,用 Cleaner 替代 |
唯一的共同点:名字很像,都以 final 开头。
面试高频追问
final修饰的引用类型变量,其内部属性可以修改吗? 可以。final只保证引用不可变,对象内容仍可修改。finally块一定会执行吗? 几乎一定。唯一例外是try或catch中调用了System.exit(),或者线程/虚拟机异常终止。- 为什么
finalize()被废弃了? 执行时间不确定、性能差、可能导致内存泄漏、可能不执行。推荐使用Cleaner(JDK 9+)或显式的close()方法。 try-with-resources和try-finally有什么区别?try-with-resources(JDK 7+)自动调用close(),代码更简洁,异常处理更合理(不会丢失原始异常)。
常见面试变体
- “被
final修饰的变量能修改吗?” - “finally 和 finalize 有什么区别?”
- “为什么
finalize()方法被废弃了?” - “try 块中有 return,finally 还会执行吗?”
记忆口诀
final:修饰不可变,类法变量三兄弟 finally:异常好搭档,资源释放保平安 finalize:回收前调用,已废弃别再用 三兄弟区别:修饰符、代码块、废弃法,名字像但无关系
总结
final、finally、finalize() 三者除了名字相似外,没有本质联系。final 是修饰符,表示 “不可变”;finally 是异常处理机制,确保代码一定执行;finalize() 是已废弃的对象回收回调方法。实际开发中,final 和 finally 经常使用,而 finalize() 应该避免使用,改用 try-with-resources 或 Cleaner 替代。
声明:本文转载自小哈的博客 《final、finally、finalize 的区别?》,版权归原文作者所有。转载请保留本声明与原文链接。
