深入理解Java中关键字final、finally、finalize的区别

 更新时间:2026年05月27日 08:37:34   作者:身如柳絮随风扬  
final、finally、finalize 的区别 这三个 Java 关键字虽然拼写相似,但功能完全不同,本文将从定义、使用场景、代码示例、底层机制以及最佳实践等多个维度,彻底讲清这三者的差异,并附上流程图和对比表格,助你一次搞懂

1. 引言

在 Java 面试中,有一个经典问题频繁出现:“请说说 final、finally 和 finalize 的区别”。这三个关键字拼写相似,但含义和作用截然不同。理解它们的区别,不仅有助于写出更健壮的代码,也是衡量 Java 基础是否扎实的重要标尺。

本文将从定义、使用场景、代码示例、底层机制以及最佳实践等多个维度,彻底讲清这三者的差异,并附上流程图和对比表格,助你一次搞懂。

2. 三者的核心区别速览

关键字所属范畴主要作用典型使用场景
final关键字/修饰符限制类、方法、变量不可变常量定义、防止继承/重写
finally异常处理块无论是否异常,保证代码执行释放资源(关闭文件、数据库连接等)
finalizeObject 类方法对象被垃圾回收前调用(已过时)对象临终清理(不推荐)

3. final:不可变的守护者

final 是一个修饰符,可以用于修饰类、方法、变量。一旦被 final 修饰,其特性便“固定”下来。

3.1 final 修饰类

final 修饰的类不能被继承。这通常用于那些设计上不允许扩展的类,例如 StringInteger 等包装类。

public final class Constants {
    // 类体
}

// 编译错误:无法继承 final 类
// class MyConstants extends Constants { }

3.2 final 修饰方法

final 修饰的方法不能被重写(Override)。这可以防止子类修改父类的关键行为。

class Parent {
    public final void doSomething() {
        System.out.println("Parent action");
    }
}

class Child extends Parent {
    // 编译错误:无法重写 final 方法
    // public void doSomething() { }
}

3.3 final 修饰变量

  • 基本类型变量:一旦赋值,值不可改变,成为常量。
  • 引用类型变量:引用地址不可改变,但对象内部状态仍可修改。
  • 成员变量:必须在声明时、构造器或初始化块中显式赋值。
  • 局部变量:在使用前赋值即可,之后不可修改。
final int MAX_COUNT = 100;          // 常量
final List<String> list = new ArrayList<>();
list.add("hello");                  // ✅ 允许(对象内容可变)
// list = new ArrayList<>();        // ❌ 编译错误,引用地址不可变

最佳实践:使用 static final 定义真正的全局常量(命名全大写、下划线分隔)。

4. finally:保证执行的最后防线

finally 是异常处理机制的一部分,与 trycatch 配合使用。它的特点是:无论 try 块中是否发生异常,finally 块中的代码都会被执行(除非 JVM 退出或当前线程被中断)。

4.1 基本语法

try {
    // 可能抛出异常的代码
} catch (Exception e) {
    // 处理异常
} finally {
    // 一定会执行的代码,通常用于释放资源
    // 例如:关闭文件流、数据库连接、网络连接等
}

4.2 典型场景:资源释放

FileInputStream fis = null;
try {
    fis = new FileInputStream("test.txt");
    // 读取文件...
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (fis != null) {
        try {
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

注意:Java 7 引入了 try-with-resources,可以更优雅地实现自动资源释放(要求资源类实现 AutoCloseable)。但 finally 仍然是通用异常清理模式的基础。

4.3 特殊情况

  • 如果 trycatch 中执行了 System.exit(0),则 finally 不会执行
  • 如果 finally 块中抛出了未被处理的异常,它会覆盖 trycatch 中抛出的原始异常。

5. finalize:被遗忘的临终方法

finalize()java.lang.Object 类中定义的一个 protected 方法。当垃圾回收器(GC)确定某个对象“没有引用”时,会先调用该对象的 finalize() 方法(如果被重写),然后再回收对象的内存。

5.1 基本用法(已过时)

public class MyResource {
    @Override
    protected void finalize() throws Throwable {
        try {
            // 释放本地资源(如关闭文件句柄)
            System.out.println("finalize called");
        } finally {
            super.finalize();
        }
    }
}

5.2 为什么 finalize 已被标记为废弃(deprecated)?

问题说明
不确定性GC 何时调用 finalize() 不可预知,甚至永远不会被调用(如果对象永远不被 GC)。
性能开销重写 finalize() 会显著增加 GC 开销(对象需要两次标记)。
顺序问题finalize() 执行顺序不受控制,可能导致资源访问冲突。
替代方案使用 try-with-resourcesCleaner(Java 9+)或显式资源管理方法(如 close())。

自 Java 9 起,finalize() 已被正式标记为 deprecated(弃用),建议永远不要在新代码中使用它。

5.3 对象死亡流程(含 finalize)

从流程图可以看出,finalize() 甚至可能导致对象“复活”(在 finalize() 中重新将自己赋值给某个静态变量),这是非常危险的行为,也是它被弃用的原因之一。

6. 综合对比与记忆口诀

6.1 对比表格

维度finalfinallyfinalize
类型关键字关键字方法名
所在包语言核心语言核心java.lang.Object
主要作用定义不可变性异常后清理对象临终处理(已过时)
可修饰目标类、方法、变量代码块对象实例方法
是否推荐使用✅ 大量使用✅ 推荐❌ 强烈不推荐

6.2 记忆口诀

final 定终(终态)身不二,finally 生死不离,finalize 来生难觅。

解释:

  • final 锁定类的继承、方法的重写、变量的改变。
  • finally 无论生死(异常与否)都会执行。
  • finalize 被垃圾回收调用,但时机不定,如今已“难觅”踪影。

7. 代码示例:三者的协作

以下示例展示了在一个方法中同时使用 final 变量、finally 块以及 finalize 方法(仅为演示,实际不推荐):

public class TestFinalFinallyFinalize {

    // final 常量
    private static final String MESSAGE = "Hello";

    public static void main(String[] args) {
        // final 局部变量
        final int times = 1;
        try {
            System.out.println(MESSAGE + " for " + times + " time");
            // 模拟异常
            // throw new RuntimeException();
        } finally {
            System.out.println("finally block: always executed");
        }
    }

    // 不推荐重写 finalize
    @Override
    protected void finalize() throws Throwable {
        System.out.println("finalize called (deprecated)");
        super.finalize();
    }
}

输出(正常情况):

Hello for 1 time
finally block: always executed

finalize() 通常不会被调用,因为程序退出时对象可能不一定会被 GC。

8. 总结

  • final:不可变修饰符,用来定义常量、阻止继承和重写。
  • finally:异常处理中的保证执行块,用于释放资源等清理工作。
  • finalize:已被废弃的对象回收前回调方法,不要在新代码中使用。

掌握这三者的区别,是 Java 基础学习中的重要里程碑。最后提醒:如果你面试时被问到 finalize,最好能主动说明它已被标记为过时,并推荐使用 try-with-resourcesCleaner 作为替代方案,这将展示你对 Java 现代特性的了解。

到此这篇关于深入理解Java中关键字final、finally、finalize的区别的文章就介绍到这了,更多相关Java final finally finalize区别内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • JavaWeb项目中springmvc和tomcat对静态文件的处理

    JavaWeb项目中springmvc和tomcat对静态文件的处理

    这篇文章主要介绍了JavaWeb项目中springmvc和tomcat对静态文件的处理 的相关资料,需要的朋友可以参考下
    2016-07-07
  • Java实现UTF-8编码与解码方式

    Java实现UTF-8编码与解码方式

    这篇文章主要介绍了Java实现UTF-8编码与解码方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-04-04
  • 深入了解JAVA泛型

    深入了解JAVA泛型

    这篇文章主要介绍了JAVA泛型的相关知识,文中代码非常详细,供大家参考和学习,感兴趣的朋友可以了解下
    2020-06-06
  • 将SpringBoot项目的HTTP站点改为HTTPS的全过程

    将SpringBoot项目的HTTP站点改为HTTPS的全过程

    本文介绍了如何在Spring Boot项目中实现HTTPS请求,首先,创建一个Spring Boot项目并编写一个BasicController.java文件,然后,配置application.yaml文件以支持HTTPS,接着,使用Java自带的keytool工具生成一个免费的HTTPS证书,需要的朋友可以参考下
    2026-02-02
  • MyBatis批量插入的三种方式比较总结

    MyBatis批量插入的三种方式比较总结

    由于项目需要生成多条数据,并保存到数据库当中,所以就用到了MyBatis批量插入,下面这篇文章主要给大家介绍了关于MyBatis批量插入的三种方式的相关资料,需要的朋友可以参考下
    2021-08-08
  • Spring @Value 设置默认值的实现

    Spring @Value 设置默认值的实现

    这篇文章主要介绍了Spring @Value 设置默认值的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • 全网最全SpringBoot集成swagger的详细教程

    全网最全SpringBoot集成swagger的详细教程

    swagger是当下比较流行的实时接口文文档生成工具,swagger分为swagger2 和swagger3两个常用版本,二者区别不是很大,主要对于依赖和注解进行了优化,swagger2需要引入2个jar包,swagger3只需要一个,用起来没有什么大的区别,本文给大家详细介绍,感兴趣的朋友一起看看吧
    2022-08-08
  • 使用Spring Security和JWT实现安全认证机制

    使用Spring Security和JWT实现安全认证机制

    在现代 Web 应用中,安全认证和授权是保障数据安全和用户隐私的核心机制,Spring Security 是 Spring 框架下专为安全设计的模块,具有高度的可配置性和扩展性,而 JWT则是当前流行的认证解决方案,所以本文介绍了如何使用Spring Security和JWT实现安全认证机制
    2024-11-11
  • java进制转换工具类实现减少参数长度

    java进制转换工具类实现减少参数长度

    这篇文章主要为大家介绍了java进制转换工具类实现减少参数长度示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-02-02
  • springboot项目使用nohup将日志指定输出文件过大问题及解决办法

    springboot项目使用nohup将日志指定输出文件过大问题及解决办法

    在Spring Boot项目中,使用nohup命令重定向日志输出到文件可能会使日志文件过大,文章介绍了两种解决方法:一是创建脚本直接清除日志文件,二是创建脚本保留部分日志内容,并将这些脚本加入定时任务中,这可以有效控制日志文件的大小,避免占用过多磁盘空间
    2024-10-10

最新评论