首页 > Java > java教程 > 正文

final, finally, finalize 三者有什么不同?

夜晨
发布: 2025-09-04 21:09:01
原创
428人浏览过
final用于定义不可变的变量、方法或类,保障不变性与安全性;finally确保异常处理中资源清理代码的执行;finalize是已被废弃的对象回收前清理方法,因不确定性与性能问题不推荐使用。

final, finally, finalize 三者有什么不同?

final
登录后复制
finally
登录后复制
finalize
登录后复制
这三个词,在Java(以及一些其他编程语言)的语境下,虽然长得像“三兄弟”,但它们各自扮演的角色和背后的设计哲学却截然不同。简单来说,
final
登录后复制
关乎不变性与限制
finally
登录后复制
确保代码的必然执行,而
finalize
登录后复制
则是一个过时且不推荐使用的资源清理机制

解决方案

这三个关键字或方法,尽管在拼写上仅一字之差,但在Java编程中却有着截然不同的用途和生命周期。理解它们的区别,是写出健壮、高效Java代码的基础。

final
登录后复制
关键字:
final
登录后复制
是一个修饰符,它用来声明一个实体是“最终的”,意味着它不能被改变、重写或继承。它的作用范围非常广:

  • 修饰变量: 当一个局部变量、实例变量或静态变量被
    final
    登录后复制
    修饰时,它就成了一个常量,一旦被赋值,其值(对于基本类型)或引用(对于对象类型)就不能再改变。这意味着你不能给它重新赋值。对于对象引用,
    final
    登录后复制
    只是保证引用本身不变,对象内部的状态依然可以改变(除非对象本身是不可变的)。
  • 修饰方法:
    final
    登录后复制
    修饰的方法不能被子类重写(Override)。这通常用于确保某个方法的行为在继承体系中保持一致,或者为了实现某些设计模式(比如模板方法模式)。
  • 修饰类:
    final
    登录后复制
    修饰的类不能被继承。这意味着它不能有子类。Java标准库中许多核心类,如
    String
    登录后复制
    Integer
    登录后复制
    等都是
    final
    登录后复制
    类,这通常是为了安全、效率或设计上的完整性考虑。

finally
登录后复制
块:
finally
登录后复制
try-catch-finally
登录后复制
语句结构中的一个代码块。它的核心作用是保证其中的代码无论如何都会被执行,无论
try
登录后复制
块中是否发生异常,或者
try
登录后复制
块是否正常结束。

  • 目的: 主要用于资源清理工作,比如关闭文件流、数据库连接、网络连接等。这样可以避免资源泄漏,即使在程序执行过程中遇到意想不到的错误。
  • 执行时机:
    finally
    登录后复制
    块会在
    try
    登录后复制
    块和
    catch
    登录后复制
    块执行之后,但在
    try
    登录后复制
    语句(或
    catch
    登录后复制
    语句)返回之前执行。即使
    try
    登录后复制
    块中有
    return
    登录后复制
    语句,
    finally
    登录后复制
    块也会先执行。只有在JVM退出或遇到
    System.exit()
    登录后复制
    等极端情况时,
    finally
    登录后复制
    块才可能不会执行。

finalize()
登录后复制
方法:
finalize()
登录后复制
java.lang.Object
登录后复制
类中定义的一个方法。它的设计初衷是作为对象被垃圾回收器(Garbage Collector, GC)回收前执行的“遗言”。

  • 目的: 理论上,可以在
    finalize()
    登录后复制
    方法中执行一些资源清理操作,比如关闭文件句柄、释放非Java内存资源等。
  • 执行时机: 它是由GC在检测到对象不再被引用时,但在真正销毁对象之前,异步调用的。
  • 问题: 现代Java编程中,
    finalize()
    登录后复制
    方法被强烈不推荐使用。原因在于其执行时机不确定、性能开销大、可能导致资源泄漏、甚至可能复活对象等诸多问题。它就像一个不可靠的“后事处理员”,你永远不知道它什么时候来,甚至它可能根本不来。

为什么
final
登录后复制
是Java中实现不变性(Immutability)的关键?

在我看来,

final
登录后复制
关键字在构建不可变对象(Immutability)方面扮演着一个基石性的角色,它不仅仅是防止变量被重新赋值那么简单,更是一种强大的设计契约。当我们说一个对象是不可变的,通常意味着它的状态在创建之后就不能再改变了。这对于编写并发程序、提高代码可读性和安全性至关重要。

final
登录后复制
变量是实现不可变性的核心。如果你有一个类,它的所有实例字段都被声明为
final
登录后复制
,并且这些字段本身也是不可变类型(例如
String
登录后复制
Integer
登录后复制
等),或者它们是可变类型但通过深拷贝(defensive copying)来确保外部无法修改内部状态,那么这个对象就具备了不可变性。比如:

public final class ImmutablePoint {
    private final int x;
    private final int y;

    public ImmutablePoint(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() { return x; }
    public int getY() { return y; }

    // 没有setter方法
    // 也没有方法可以改变x或y的值
}
登录后复制

在这个

ImmutablePoint
登录后复制
例子中,
x
登录后复制
y
登录后复制
final
登录后复制
修饰,一旦构造函数赋值后,它们的值就不能再变了。同时,类本身被
final
登录后复制
修饰,防止被继承,进一步巩固了其不可变性。

不可变性带来的好处是巨大的:

  • 线程安全: 不可变对象天生就是线程安全的,因为它们的状态不会改变,多个线程可以同时访问而无需担心同步问题。这大大简化了并发编程
  • 可预测性: 一旦创建,对象的状态就固定了,这使得代码更容易理解和推理,减少了意外副作用的可能性。
  • 缓存: 不可变对象可以安全地被缓存,因为你不需要担心它们会在缓存之外被修改。
  • 哈希表键: 它们是作为
    HashMap
    登录后复制
    HashSet
    登录后复制
    的键的理想选择,因为它们的哈希码在整个生命周期中保持不变。

所以,

final
登录后复制
不仅仅是一个语法糖,它是一种编程思想的体现,它帮助我们构建更健壮、更易于维护的系统。

finally
登录后复制
块在异常处理中扮演着怎样的角色,它与
try-with-resources
登录后复制
有何关联?

finally
登录后复制
块在Java的异常处理机制中,扮演着“无论如何都要完成任务”的角色。它的主要职责是确保在
try
登录后复制
块(无论是否发生异常)或
catch
登录后复制
块执行后,某些关键的清理代码能够被执行。这对于资源管理至关重要,比如关闭文件、数据库连接、网络套接字等,以防止资源泄漏。

歌者PPT
歌者PPT

歌者PPT,AI 写 PPT 永久免费

歌者PPT 197
查看详情 歌者PPT

想象一下,如果你打开了一个文件,但在处理过程中抛出了异常,如果没有

finally
登录后复制
块来关闭文件,那么文件句柄就会一直被占用,最终可能导致系统资源耗尽。

一个典型的

finally
登录后复制
使用场景是这样的:

FileInputStream fis = null;
try {
    fis = new FileInputStream("file.txt");
    // 读取文件内容...
    // 假设这里可能抛出IOException
} catch (IOException e) {
    System.err.println("文件操作失败: " + e.getMessage());
} finally {
    if (fis != null) {
        try {
            fis.close(); // 确保文件流被关闭
        } catch (IOException e) {
            System.err.println("关闭文件流失败: " + e.getMessage());
        }
    }
}
登录后复制

这段代码虽然有效,但存在一些冗余,特别是关闭资源的逻辑需要嵌套

try-catch
登录后复制
,使得代码显得有些笨重。

这时候,Java 7 引入的

try-with-resources
登录后复制
语句就如同救星一般出现了。它是一种语法糖,专门用来简化那些实现了
java.lang.AutoCloseable
登录后复制
接口的资源的自动管理。它的核心思想是:只要在
try
登录后复制
关键字后面的括号中声明并初始化资源,这些资源就会在
try
登录后复制
块执行完毕后(无论正常结束还是异常退出)被自动关闭,无需显式地编写
finally
登录后复制
块。

使用

try-with-resources
登录后复制
,上面的例子可以被极大地简化和优化:

try (FileInputStream fis = new FileInputStream("file.txt")) {
    // 读取文件内容...
    // 假设这里可能抛出IOException
} catch (IOException e) {
    System.err.println("文件操作失败或关闭文件流失败: " + e.getMessage());
}
登录后复制

可以看到,

try-with-resources
登录后复制
极大地提升了代码的简洁性和可读性,同时确保了资源的正确释放。它在底层其实就是编译器帮我们自动生成了一个
finally
登录后复制
块来关闭资源。所以,可以说
try-with-resources
登录后复制
finally
登录后复制
块在特定场景下的一种更优雅、更安全的替代方案,它避免了手动管理资源的繁琐和潜在错误。在现代Java编程中,只要资源实现了
AutoCloseable
登录后复制
接口,都应该优先考虑使用
try-with-resources
登录后复制

Java的
finalize()
登录后复制
方法为什么被强烈不推荐使用,它有哪些潜在的问题?

finalize()
登录后复制
方法在Java的早期版本中,被设计为一种在对象被垃圾回收器(GC)销毁前执行清理操作的机制。听起来很美好,对吧?一个对象在“临终”前还能做点什么。但实践证明,这个方法带来的问题远多于其解决的问题,因此在现代Java编程中,它被强烈不推荐使用,甚至可以说,除非你对JVM和GC有极其深入的理解,并且有非常特殊的需求,否则永远不要碰它。

finalize()
登录后复制
方法的潜在问题主要体现在以下几个方面:

  1. 执行时机不确定性: 这是最致命的问题。
    finalize()
    登录后复制
    方法的调用是由GC决定的,而GC的运行是异步的、非确定性的。你无法预测一个对象的
    finalize()
    登录后复制
    方法何时会被调用,甚至不能保证它一定会被调用(比如程序在GC运行前就退出了)。这意味着你不能依赖它来释放关键资源,因为资源可能长时间得不到释放,导致泄漏。
  2. 性能开销: 拥有
    finalize()
    登录后复制
    方法的对象,在垃圾回收过程中需要额外的处理。GC在回收这些对象时,需要将它们放入一个特殊的队列,等待一个单独的线程去执行
    finalize()
    登录后复制
    方法。这会增加GC的负担,降低回收效率,影响应用程序的整体性能。
  3. 可能导致资源泄漏: 如果
    finalize()
    登录后复制
    方法中出现异常,并且这个异常没有被捕获,那么这个异常会被忽略,但
    finalize()
    登录后复制
    线程可能会终止,导致其他等待执行
    finalize()
    登录后复制
    方法的对象永远得不到清理。
  4. 对象复活(Resurrection):
    finalize()
    登录后复制
    方法中,你可以让一个对象重新被引用,从而阻止它被回收。这被称为“对象复活”,它使得对象的生命周期变得极其复杂和难以预测,是典型的反模式。
  5. 不确定性导致的调试困难: 由于其执行时机和行为的不可预测性,一旦出现与
    finalize()
    登录后复制
    相关的问题,调试将变得异常困难。你很难重现问题,也难以追踪问题根源。
  6. 与现代资源管理机制的冲突: 现代Java已经有了更好的资源管理方式,比如前面提到的
    try-with-resources
    登录后复制
    语句,以及更底层的
    java.lang.ref.Cleaner
    登录后复制
    (用于更复杂的非Java内存资源清理场景)。这些机制提供了确定性、高效且安全的资源释放方式,完全取代了
    finalize()
    登录后复制
    的作用。

我的建议是:忘掉

finalize()
登录后复制
方法吧。 如果你需要清理资源,请使用
try-with-resources
登录后复制
。如果资源不属于
AutoCloseable
登录后复制
范畴,那么请提供一个显式的
close()
登录后复制
方法,并在使用完毕后手动调用它。这才是确保资源被及时、正确释放的可靠途径。
finalize()
登录后复制
就像一个被遗弃的旧工具,虽然还在那里,但已经无人问津,因为它带来的麻烦远超其价值。

以上就是final, finally, finalize 三者有什么不同?的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
热门推荐
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号