final、finally、finalize有什么区别?

引言

在Java编程语言中,final、finally和finalize是三个具有不同用途和语义的关键字或方法。它们在编程和面试中经常被提及,因此理解它们之间的区别是非常重要的。

题目

final、finally、 finalize有什么区别?

典型回答

  • final
    修饰类:当一个类被声明为 final 类时,意味着它不能被其他类继承,这样可以保护类的完整性和设计意图,防止外部对类的不恰当扩展。
    修饰方法:final 方法在类中声明后,子类无法覆盖或重写这个方法,确保方法的行为在派生类中始终保持不变。
    修饰变量:无论是成员变量还是局部变量,声明为 final 后,其值在初始化之后就不允许被修改。对于引用类型,final 保证了引用地址不可变,但引用的对象状态仍然可以改变。
  • finally
    在Java异常处理机制中,finally块内的代码一定会被执行,无论try块内是否有异常被捕获或是程序执行流程如何跳转。这一特性常用于资源清理场景,确保诸如数据库连接、文件流等资源在程序结束后能得到及时正确的释放,避免资源泄露。
  • finalize
    finalize 是 Object 类所提供的一个方法,其初衷是给开发者提供一个在对象即将被垃圾收集器回收之前进行最后一次清理的机会。然而,由于其执行时机不确定、可能导致性能瓶颈以及容易引起编程错误等问题,现今已不推荐使用finalize方法来管理资源,从JDK 9开始,Object类的finalize()方法被标记为deprecated。推荐的做法是利用try-with-resources语句、Cleaner类等现代Java资源管理工具来确保资源的正确释放。

加分项

final实践及注意问题

面试官除了希望了解应聘者对Java基础知识的掌握程度外,更期待听到他们对这些基础知识的个人见解和在实际项目中的应用经验。通过分享对性能优化、并发编程、对象生命周期以及垃圾收集基本过程等方面的理解,应聘者能够展现出自己的深入思考和实际操作能力,从而更全面地展示自己作为Java程序员的实力。
编程实践中充分利用final关键字来清晰表达代码的含义和逻辑目标,这一举措已在众多应用场景中展现出其优越性。
例如,通过将方法或类声明为final,开发者能够明确传达给其他团队成员及后续维护者,此类行为或功能是不可篡改的,从而确保了代码的稳定性和一致性。
深入研究Java的核心类库,我们会发现在java.lang包下,许多至关重要的类都被明智地声明为了final类。同样的现象在第三方类库的基础组件中也不鲜见,这样做有效地防止了API用户对核心功能的任意改动,从而在一定程度上加固了平台的安全性和可靠性。
此外,对于方法参数、局部变量乃至成员变量,适时使用final修饰符同样具有重要意义。这样做不仅可以明显减少因意外赋值引发的程序错误,某些编程规范甚至提倡将所有可能的情况都声明为final,以此强化代码的严谨性。
特别是在并发编程情境下,final变量具备了某种程度的不可变(immutable)特性,它能够很好地保护只读数据免受意外修改。由于final变量一旦初始化后不可再赋新值,所以在多线程环境下,程序员可以不必为final变量的同步操心,这无疑减轻了同步控制的负担,同时也规避了进行不必要的防御性复制操作,进而提升了代码的简洁性和效率。

finally实践及注意问题

finally块在Java编程实践中主要用于确保一段代码无论在何种情况下都能得到执行,特别是当代码块包含资源清理逻辑时,例如关闭数据库连接、网络套接字或者释放锁等操作。以下是finally实践中的几个重要注意事项:

  1. 异常处理: finally块常与try-catch一起使用,无论try块中是否抛出了异常,finally块中的代码都将被执行。这意味着无论异常是否被捕获或传播,finally总能完成资源的正确关闭或释放。
  2. 资源关闭的最佳实践:自从Java 7引入了try-with-resources语句后,对于实现了AutoCloseable接口的资源,如JDBC连接或文件流,更推荐使用try-with-resources来代替传统的try-finally结构,因为它能自动管理资源的关闭,减少了手动编写finally块的必要性,且代码更简洁。
  3. 避免副作用:在finally块中,应当避免对控制流产生意想不到的副作用,例如改变程序的执行路径或提前退出程序(如使用System.exit())。这是因为finally块的执行并不依赖于try或catch块中的逻辑,而是无条件执行的。
  4. 异常抑制:在finally块中抛出异常时,如果不处理这个异常,原来try或catch块中的异常将被这个新的异常所取代,原有异常信息可能会丢失。为了避免这种情况,通常不在finally中抛出新的异常,除非是有意要忽略之前的异常并报告更重要的错误。
  5. 代码执行顺序:finally块会在try和catch块执行完毕后立即执行,不受return、throw等语句的影响。但是在finally块执行完毕后,原先try或catch块中发生的异常(如果有的话)将继续向外传播。

总之,在使用finally时,应始终牢记其目的是确保资源的安全释放,同时尽可能减少对程序控制流和异常处理逻辑的干扰,以保持代码的清晰性和可读性。

finalize实践及注意问题

在Java编程中,finalize方法曾经被视为一种资源回收机制,允许对象在被垃圾收集器回收前执行最后一次清理操作。然而,由于finalize方法存在诸多问题和不确定性,业界已经广泛认同其不是一个理想的资源管理策略,并在Java 9版本中将Object.finalize()方法明确标注为过时(deprecated)。
finalize方法不推荐使用的原因主要包括:

  1. 执行时间不确定:Java虚拟机(JVM)并不保证finalize方法何时会被调用,甚至可能永远不会被调用。这意味着依赖finalize方法来清理重要资源极其不可靠。
  2. 性能影响严重:实现finalize方法的对象在垃圾收集过程中,会被当作特殊的对象处理,增加了GC的复杂度和延迟,可能导致性能大幅降低。
  3. 容易引发死锁和挂起:由于finalize方法的执行时机与垃圾回收紧密相连,它可能在不恰当的时候被触发,从而引发死锁或者其他并发问题。
  4. 不利于调试和错误处理:finalize方法中抛出的异常会被JVM默默吞掉,且无法保证其清理逻辑的执行效果,这对排查问题和确保资源释放极为不利。

相比之下,推荐使用更安全和高效的资源管理方式,如try-with-resources语句,它可以确保在资源使用完毕后立即关闭,大大减少了资源泄漏的风险。另外,对于复杂的资源清理任务,可以利用Java提供的Cleaner类,它通过PhantomReference实现了更为可控和安全的清理机制,相比finalize更可靠和易于管理。

知识扩展

final 不是 immutable?

当你声明一个变量为 final 类型时,如 final List<String> strList,它仅仅意味着你不能重新给 strList 变量赋予新的引用。换句话说,你不能将 strList 指向一个不同的 List 对象。然而,这并不意味着 strList 所指向的对象(即 List 本身)的行为受到 final 关键字的约束,因此你仍然可以往这个 List 中添加、删除元素等进行修改操作。
举例来说,尽管你创建了一个 final List:

final List<String> strList = new ArrayList<>();

你可以继续调用 add 方法往这个 List 添加元素,因为这只是修改 List 内部状态,而不是改变 strList 引用指向的地址。
若要创建一个不可变的 List,需要依赖于支持不可变行为的类或集合,如 Java 9 引入的 List.of() 方法,它返回的 List 是不可变的,因此试图对其执行添加元素的操作会抛出 UnsupportedOperationException 异常。这意味着,使用 List.of("hello", "world") 创建的 List 无法通过 add 方法添加新的元素。
为了实现一个不可变(immutable)的Java类,你需要遵循以下设计原则和步骤:

  1. 声明类为final:首先,将类声明为final,防止其他类对其进行扩展,确保类的不变性属性不能被子类破坏。
  2. 使用private final成员变量:所有的成员变量应当声明为private和final,这意味着它们一旦在构造函数中初始化后就不能再被修改。
  3. 禁用setter方法:不可变类不应该提供任何修改其状态的setter方法,确保对象创建后其内部状态永远不会再改变。
  4. 深度拷贝初始化:在构造对象时,如果成员变量是引用类型,尤其是可变对象的引用,应采取深度拷贝的方式来初始化这些成员变量,以确保即使是可变对象的内容也不会影响到不可变类实例的状态。
  5. 实现安全的getter方法:如果类需要公开其内部状态,可以通过getter方法来获取,但对于引用类型的成员变量,应谨慎处理,避免直接返回引用。在可能的情况下,可以返回成员变量的副本(浅拷贝或深拷贝),或者对于集合类等,可以返回不可变视图(如Collections.unmodifiableList())。
  6. 考虑Copy-On-Write机制:在某些情况下,如果类需要支持修改操作,而又想保持不可变性质,可以采用Copy-On-Write(COW)策略。每当需要修改内部状态时,先创建现有对象的一个私有副本,然后在副本上进行修改操作,并返回新的不可变对象,而不是直接修改原始对象。

通过以上设计,可以确保类的实例在其整个生命周期内状态都不会发生变化,从而实现不可变性。这种不可变对象在多线程环境中尤为安全,因为它们不需要同步就能保证线程安全,并且可以轻易地作为缓存项使用,因为它们的哈希码可以在创建时计算并一直保持不变。

finalize 一无是处?

finalize方法之所以被认为是一种糟糕的实践,是因为它与垃圾收集(GC)过程紧密结合,带来了若干重大问题:

  1. 性能下降:当一个类实现了非空的`finalize`方法,该对象的垃圾收集速度会显著降低,根据基准测试,其回收速度可能会减慢几十倍之多。这意味着在高负载或内存紧张的系统中,垃圾回收会变得更加缓慢和低效。
  2. 不可预测性:`finalize`方法在对象被垃圾收集前调用,但具体的调用时间由JVM决定,具有很大的不确定性。即使通过`System.runFinalization()`方法强制执行,也不能确保所有待回收对象的`finalize`方法立刻执行完毕。
  3. 回收延迟:带有`finalize`方法的对象在垃圾收集时被当作“特殊公民”,JVM需要对它们进行额外处理,这会导致这类对象可能经历多次垃圾收集周期才得以真正回收,从而可能导致内存占用过高,增加OOM(Out Of Memory)的风险。
  4. 资源管理风险:由于垃圾收集时间的不可预测性,依赖`finalize`方法来释放关键资源是极其危险的。在高并发环境或对资源敏感的应用中,未及时释放资源会迅速耗尽系统资源,严重影响系统稳定性。

因此,专家推荐尽量避免使用finalize方法来处理资源释放,而应该采用更直接、明确的资源管理策略,如显式调用资源的close或dispose方法,或者使用Java 7引进的try-with-resources语句来确保资源的及时回收。对于高频使用的资源,更是推荐采用资源池技术以实现资源的有效重用。

finalize 替代方案?

Java平台为了克服finalize方法在资源回收方面的不足,逐渐倾向于使用java.lang.ref.Cleaner类作为替代方案。Cleaner类利用了Java中的高级内存管理机制——幻象引用(PhantomReference),这是一种特殊的弱引用,它能够在对象不可达且即将被垃圾收集器回收的“post-mortem”阶段进行清理操作。
相比于finalize,Cleaner机制具有如下优点:

  1. 更轻量级:Cleaner的实现相对于finalize而言更加轻量,不会像finalize那样显著增加垃圾回收的复杂性和延迟,从而提高了整体的性能表现。
  2. 更可靠:finalize的执行时机不确定,有时可能并不会按预期执行,而Cleaner则通过引用队列机制确保在对象被垃圾回收器清除之前执行清理操作,因此更具有可靠性。
  3. 避免死锁:Cleaner针对每个待清理的任务都有独立的运行线程,这有助于避免因finalize方法执行期间可能引发的死锁问题,提高了并发环境下的安全性。

通过Cleaner,开发者可以确保在对象被垃圾收集器最终回收之前,操作系统级别的资源(如文件描述符等)得到妥善释放,从而降低了资源泄露的风险,并提高了程序的整体健壮性和稳定性。在后续的教程或专栏中,将进一步详细介绍Java中各种引用类型,包括幻象引用及其在资源回收中的具体应用。

其它

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/586695.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ZooKeeper 搭建详细步骤之二(伪集群模式)

ZooKeeper 搭建详细步骤之三&#xff08;真集群&#xff09; ZooKeeper 搭建详细步骤之二&#xff08;伪集群模式&#xff09; ZooKeeper 搭建详细步骤之一&#xff08;单机模式&#xff09; ZooKeeper 及相关概念简介 伪集群搭建 ZooKeeper 伪集群是指在一个单一的物理或虚拟…

活动回顾 | 春起潮涌——硬件驱动的量化交易与AI

4月20日&#xff0c;华锐技术ACLUB联合AMD在上海举办了“春起潮涌——硬件驱动的量化交易与AI”沙龙活动&#xff0c;会议围绕FPGA硬件加速、CPU&网卡调优、AI技术应用等展开&#xff0c;近50位量化IT与分享嘉宾一起探讨硬件技术在量化交易和AI领域的应用和创新。 FPGA在交…

云服务器把端口添加到安全组后无法访问

直接 sudo iptables -I INPUT 5 -p tcp --dport 8085 -j ACCEPT 8085就是端口号 然后再运行服务器 就成功了

YOLOv5入门(二)处理自己数据集(标签统计、数据集划分、数据增强)

上一节中我们讲到如何使用Labelimg工具标注自己的数据集&#xff0c;链接&#xff1a;YOLOv5利用Labelimg标注自己数据集&#xff0c;完成1658张数据集的预处理&#xff0c;接下来将进一步处理这批数据&#xff0c;通常是先划分再做数据增强。 目录 一、统计txt文件各标签类型…

【C语言】——数据在内存中的存储

【C语言】——数据在内存中的存储 一、整数在内存中的存储1.1、整数的存储方式1.2、大小端字节序&#xff08;1&#xff09;大小端字节序的定义&#xff08;2&#xff09;判断大小端 1.3、整型练习 二、浮点数在内存中的存储2.1、引言2.2、浮点数的存储规则2.3、浮点数的存储过…

OI Wiki—递归 分治

//新生训练&#xff0c;搬运整理 递归 定义 递归&#xff08;英语&#xff1a;Recursion&#xff09;&#xff0c;在数学和计算机科学中是指在函数的定义中使用函数自身的方法&#xff0c;在计算机科学中还额外指一种通过重复将问题分解为同类的子问题而解决问题的方法。 引入…

完美解决AttributeError: module ‘backend_interagg‘ has no attribute ‘FigureCanvas‘

遇到这种错误通常是因为matplotlib的后端配置问题。在某些环境中&#xff0c;尤其是在某些特定的IDE或Jupyter Notebook环境中&#xff0c;可能会因为后端配置不正确而导致错误。错误信息提示 module backend_interagg has no attribute FigureCanvas 意味着当前matplotlib的后…

基于STC12C5A60S2系列1T 8051单片机的Proteus中的单片机发送一帧或一串数据给串口调试助手软件接收区显示出来的串口通信应用

基于STC12C5A60S2系列1T 8051单片机的Proteus中的单片机发送一帧或一串数据给串口调试助手软件接收区显示出来的串口通信应用 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机串口通信介绍STC12C5A60S2系列1T 8051单片机串口通信的结构基于STC12C5A60S2系列…

【MyBatis】 MyBatis框架下的高效数据操作:深入理解增删查改(CRUD)

&#x1f493; 博客主页&#xff1a;从零开始的-CodeNinja之路 ⏩ 收录文章&#xff1a;【MyBatis】 MyBatis框架下的高效数据操作&#xff1a;深入理解增删查改&#xff08;CRUD&#xff09; &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 目录 My …

工具分享:免费一键生成像素风格头像神器

目录 引言神器介绍使用方法上传照⽚选择像素大小保存or分享图片生后图像处理功能娱乐功能 结语最后 引言 五一前一天和群友聊到换微信头像的事情&#xff0c;我就心想自己制作一些头像来用吧&#xff0c;起初是用的无界AI通过咒语来生成头像&#xff0c;但总不尽如人意。如下&…

TFLOPS和TOPS介绍

TFLOPS和TOPS都是衡量计算设备性能的单位&#xff0c;常用于评估处理器或加速器在科学计算、图形处理以及人工智能领域的运算能力。它们分别代表不同的运算类型&#xff1a; TFLOPS (Tera Floating Point Operations Per Second) TFLOPS用于衡量每秒执行的万亿次浮点运算数。…

「 网络安全常用术语解读 」软件物料清单SBOM详解

1. 概览 软件物料清单&#xff08;Software Bill of Materials&#xff0c;SBOM&#xff09;是软件成分信息的集合&#xff0c;SBOM文件中记录了软件产品或服务所使用组件、库、框架的清单&#xff0c;用于描述软件构建过程中使用的所有组件及其关系&#xff0c;以实现软件供应…

spring的高阶使用技巧1——ApplicationListener注册监听器的使用

Spring中的监听器&#xff0c;高阶开发工作者应该都耳熟能详。在 Spring 框架中&#xff0c;这个接口允许开发者注册监听器来监听应用程序中发布的事件。Spring的事件处理机制提供了一种观察者模式的实现&#xff0c;允许应用程序组件之间进行松耦合的通信。 更详细的介绍和使…

22 重构系统升级-实现不停服的数据迁移和用户切量

专栏的前 21 讲&#xff0c;从读、写以及扣减的角度介绍了三种特点各异的微服务的构建技巧&#xff0c;最后从微服务的共性问题出发&#xff0c;介绍了这些共性问题的应对技巧。 在实际工作中&#xff0c;你就可以参考本专栏介绍的技巧构建新的微服务&#xff0c;架构一个具备…

【Schrödinger薛定谔软件使用实战】- 4lyw蛋白实战

文章目录 软件选择1 pretein preparation1.1 import and process注意1.1.1 preprocess可能遇到的问题 1.2 review and modify1.3 refine1.3.1 optimize优化氢键网络1.3.2 minimize 氢原子会进行能量最小化 2 ligand prepare3 生成对接盒子-receptor grid generation3.1 recepto…

Q1营收稳健增长,云从科技如何在“百模大战”的险中求稳?

自从迈入大模型时代&#xff0c;AI行业可谓“一天一个样”。越来越多的企业涌现&#xff0c;舆论热议从未断绝。 但就像所有技术必须经历的那些考验&#xff0c;在现实尺度下&#xff0c;AI顺利走进商业化世界&#xff0c;仍然是少部分玩家掌握的稀缺能力。个中原因不尽相同&a…

第49期|GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以找…

javase学习01-GUI设计中的菜单条,菜单及菜单项(简单的实现)

目录 一&#xff0c;效果及代码 二&#xff0c;相关内容 1&#xff0c;创建图片资源文件夹 2&#xff0c;菜单初识 3&#xff0c;图标大小设置 4&#xff0c;菜单高度设置 5&#xff0c;设置窗口的图标 ☀ 今天学习了Java的GUI&#xff08;graphics user interface&…

C++入门基础(二)

目录 缺省参数缺省参数概念缺省参数分类全缺省参数半缺省参数声明与定义分离 缺省参数的应用 函数重载函数重载概念例子1 参数类型不同例子2 参数的个数不同例子3 参数的顺序不同 C支持函数重载的原理--名字修饰(name Mangling) 感谢各位大佬对我的支持,如果我的文章对你有用,欢…

报错“Install Js dependencies failed”【鸿蒙开发Bug已解决】

文章目录 项目场景:问题描述原因分析:解决方案:此Bug解决方案总结Bug解决方案寄语项目场景: 最近也是遇到了这个问题,看到网上也有人在询问这个问题,本文总结了自己和其他人的解决经验,解决了【报错“Install Js dependencies failed”】的问题。 报错如下 问题描述 …
最新文章