Java 中 final、finally 与 finalize():语义、底层机制与常见误区
目录一. finalize() 详解1.1 finalize 方法是什么1.2 finalize 方法的作用1.3 为什么强烈不建议在 finalize 方法中进行资源的释放和清理工作1.4 版本说明二. finally 详解2.1 基本语义2.2 return 遇到 finally2.3 finally 也可能掩盖原始异常2.4 finally 也并非绝对执行三. final 关键字详解3.1 修饰类3.2 修饰方法3.3 修饰字段3.4 修饰基本类型变量和引用类型变量3.5 修饰局部变量3.6 修饰方法参数3.7 final 不保证线程安全3.8 static final 修饰的不一定是编译期常量先说结论finalfinallyfinalize 三者没有直接语义关联final编译器约束与JVM语义finally异常控制与资源清理finalize()对象终结机制及其淘汰策略一. finalize() 详解1.1 finalize 方法是什么首先要知道finalize 方法是 Object 类中的一个方法从源码中看到所以所有的对象都会具有这个方法该方法修饰符为 protected 子类可以进行 Override 重写但实际开发过程中一般不在意在 JDK9 之后该方法已经被官方不推荐使用了但面试的时候还是有可能会被问到的所以我们就来聊聊该方法的一些作用。1.2 finalize 方法的作用finalize()是旧版 Java 提供的对象终结回调。当 JVM 发现一个重写了finalize()的对象已经不可达时可能在回收其存储空间前调用该方法。但调用时间不确定也不保证一定执行。即使对象不可达也可能因为 JVM 很快退出等原因从未执行finalize()。当我们将一个对象设置为 null 之后它和对象的引用关系就断开了若被对象不能再从任何 GC Roots 通过引用链到达时它才可能被判定为不可达对象虽然 finalize()历史上曾被设计用于对象终结和资源清理但实践证明这种清理方式缺乏确定性不应承担文件、Socket、数据库连接等资源的释放工作。如下代码所示public class Test { // 重写 Test 类中的 finalize() 方法 Override public void finalize() throws Throwable{ // 打印一句话作为标记证明该方法被调用过 System.out.println(finalize方法执行); } public static void main(String[] args) throws Exception { // 创建类对象 t Test t new Test(); // 获取该对象 System.out.println(t第一次获取对象); // 断开局部变量 t 与对象之间的引用 t null; // 仅向 JVM 提出执行垃圾回收的建议不保证立即发生 GC // 也不保证 finalize() 一定被调用。 System.gc(); // 仅为了提高观察到 finalize() 输出的概率不提供任何执行保证 Thread.sleep(2000); // 经过GC之后再次获取t对象 System.out.println(t第二次获取对象); } }垃圾回收和对象终结由 JVM 内部机制处理与业务线程一部运行执行时机独立于业务线程。程序退出时JVM 不保证等待所有待终结对象执行 finalize()然后我们运行上述方法在控制台中得到如下所示结果手动调用GCJVM 接受了System.gc()请求并调用了该对象的finalize()。该结果只能说明本次运行观察到了终结回调不能证明将引用赋值为null后一定会执行finalize()。1.3 为什么强烈不建议在 finalize 方法中进行资源的释放和清理工作原因我总结了以下三点1资源释放实际成谜如果我们重写了 finalize() 方法那么对象在变成垃圾被垃圾回收器回收之前需要先执行 finalize() 方法而 finalize() 方法并不是由垃圾回收器来调用的是有一个专门的线程叫 finalizer 来调用的finalizer 线程内部有一个引用队列垃圾回收器识别到垃圾对象之后会把该对象添加到引用队列中去finalizer 线程会依次调用每个对象的 finalize() 方法然后进行清理。但是垃圾回收时机不确定终结队列的处理速度也不可控因此外部资源的释放时间无法得到保证。文件描述符、Socket 和数据库连接属于稀缺资源不能等待不确定的 GC 周期进行释放。2资源释放异常finalize()既然是一个方法那么它就有可能抛出异常如果finalize()抛出未捕获异常该异常通常会被终结机制忽略本次finalize()调用随之结束。对象不会仅仅因为该异常就永久无法回收但预期的资源清理逻辑可能没有完整执行。3很容易导致OOM(内存溢出)GC垃圾回收线程和主程序的线程不是同一个线程。也就是说我们的主线程也可以说是业务一直都在持续地进行着那么业务就很有可能会在内存中继续创建出新的对象进行其他的业务操作。而此时先前创建的对象被回收需要调用 finalize 方法如果我们将一些资源的释放工作和内存清理工作全部放到 finalize 方法中的话是非常耗时的特别是网络断开连接操作需要等待对方的响应才能断开连接但现在内存中的垃圾对象又需要等待 finalize 方法执行完毕后才能清除由于业务的不断进行还有不断新的对象被创建垃圾对象的清理速度慢新对象的创建速度快清理的速度没有创建的速度快finalize()执行缓慢或发生阻塞待终结对象可能在队列中持续积压。这些对象及其关联数据暂时无法释放从而增加内存压力严重时可能导致OutOfMemoryError。1.4 版本说明finalize() 从JDK9开始标记为弃用JDK18通过 JEP 421 将终结机制标记为弃用并移除计划。新代码不应再使用这一机制。二. finally 详解2.1 基本语义finally表示退出try或catch前需要执行的收尾逻辑。try可以直接和finally组合不强制要求存在catchlock.lock(); try { process(); } finally { lock.unlock(); }无论 process() 正常返回还是抛出异常都应该释放锁2.2 return 遇到 finallystatic int test() { int value 1; try { return value; } finally { value 2; } }结果仍然是 1执行 return value 时返回值已经被计算并暂存随后才执行 finally。但是如果 finally 自己也执行 returnstatic int test() { try { return 1; } finally { return 2; } }结果为2。 finally 的突然完成会覆盖此前的返回或异常因此严禁在 finally 中使用 return2.3 finally 也可能掩盖原始异常try { throw new IllegalStateException(业务异常); } finally { throw new RuntimeException(清理异常); }上方代码执行后调用方看到的是清理异常原始业务异常被掩盖。这也是 try-with-resources 更可靠的原因如果业务代码和 close() 都抛出异常业务异常作为主异常关闭资源产生的异常会通过 getSuppressed() 保存。2.4 finally 也并非绝对执行在以下情况下finally 不保证执行System.exit() 或 Runtime.halt()JVM或操作系统异常终止进程被强制杀死机器断电控制流永远无法离开 try例如死循环或永久阻塞因此finally 能保证的是 Java 控制流正常离开响应代码区域的收尾不是进程级别的绝对保证。三. final 关键字详解3.1 修饰类当 final 修饰类时表示该类不可被继承。将类声明为final是实现不可变类的一种手段但final类并不必然是不可变类。String的不可变性还依赖字段封装、不提供修改内部状态的方法以及对内部数据的安全处理。因此finall 类 ≠ 不可变类final 和 abstract 不能同时修饰一个类因为前者禁止继承而后者要求通过继承实现。需要强调final 类不等于类中的所有字段都是常量它只是禁止继承。3.2 修饰方法final实例方法不能被子类重写但仍然可以被重载。构造器不能使用final修饰因为构造器本身不参与继承和重写。final与abstract也不能同时修饰一个方法。class Parent { public final void execute() { } public void execute(String command) { // 这是重载不是重写 } }3.3 修饰字段观察下方关于 final修饰实例字段(nameage)和静态字段(MAX_SIZE)的相关代码class Example { static final int MAX_SIZE; final String name; final int version; static { MAX_SIZE 100; } { version 1; } Example(String name) { this.name name; } }static final 修饰的字段必须在声明处或静态初始化块中完成初始化final 实例字段必须在声明处、实例初始化块或者每一条构造路径中完成初始化每个空白final字段必须在规定的初始化阶段完成一次赋值并且不能在后续代码中再次赋值编译器会通过“确定赋值分析”检查变量是否在使用前完成赋值以及是否可能被重复赋值3.4 修饰基本类型变量和引用类型变量final限制的是变量的再次赋值而不是对象的可变性。对于引用变量不能改变它保存的引用但被引用对象的内部状态仍然可能改变。基本类型变量 number 保存数值10引用类型变量names保存ArrayList对象的引用。final对两者的约束都是禁止再次赋值但具体表现有所不同。用一句话总结概括就是final限制的是变量不能被再次赋值。对于引用类型变量变量中保存的对象引用不能被替换但该引用指向的对象仍然可能是可变的final int number 10; // number 20; // 不允许 final ListString names new ArrayList(); names.add(Alice); // 允许修改对象状态 // names new ArrayList(); // 不允许更换引用3.5 修饰局部变量result 虽然没有在声明时初始化但每条分支都恰好赋值一次因此可以通过编译void process(boolean success) { final int result; if (success) { result 1; } else { result 0; } System.out.println(result); }3.6 修饰方法参数其实和上面基本类型变量、引用类型变量的规则差不多方法参数也可以为 final但它只能防止方法内部重新给参数赋值不能阻止外部对象被修改void update(final String name) { // name newName; // 不允许重新赋值 } void update(final ListString names) { names.add(Alice); // 允许 // names new ArrayList(); // 不允许 }3.7 final 不保证线程安全道理很简单final 只保证对象的引用不可变但是高并发场景下引用的对象比如集合进行并发 add() 修改是线程不安全的3.8 static final 修饰的不一定是编译期常量只有使用常量表达式初始化的基本类型或 String 类型的 static final 字段才属于编译期常量static final int MAX_SIZE 100; // 编译期常量 static final Integer RANDOM new Random().nextInt(); // 不是编译期常量

相关新闻

数据库 explain 关键字解析

数据库 explain 关键字解析

目录 一. explain 概述 二. explain 关键字的使用方式 三. explain 的版本迭代 四. explain 只分析SQL语句,不执行SQL语句 五. explain 输出结果中各个字段的含义 六. type 表示检索表数据的方式 七. key_len表示使用的索引的长度 八. rows 表示预估读取到的…

2026/7/16 18:29:37阅读更多 →
常用资源环境生态地理空间数据开源下载地址整理

常用资源环境生态地理空间数据开源下载地址整理

上学时研究需要用到大量资源环境生态地理空间数据,曾经搜集了不少资料,但开源的数据鱼龙混杂,参差不齐,数据筛选、验证和使用花费了不少时间,着实需要整理一下,在此记录一下已经整理过的数据!有…

2026/7/16 18:24:37阅读更多 →
深入理解bytebuffer.js的API设计:flip、mark与reset方法的高级应用

深入理解bytebuffer.js的API设计:flip、mark与reset方法的高级应用

深入理解bytebuffer.js的API设计:flip、mark与reset方法的高级应用 【免费下载链接】bytebuffer.js A fast and complete ByteBuffer implementation using either ArrayBuffers in the browser or Buffers under node.js. 项目地址: https://gitcode.com/gh_mirr…

2026/7/16 18:24:37阅读更多 →
WMPageController 使用教程

WMPageController 使用教程

WMPageController 使用教程 【免费下载链接】WMPageController An easy solution to page controllers like NetEase News 项目地址: https://gitcode.com/gh_mirrors/wm/WMPageController 1. 项目介绍 WMPageController 是一个开源的 iOS 项目,旨在为开发者…

2026/7/16 19:24:41阅读更多 →
Qt,C++海康2D工业相机二次开发

Qt,C++海康2D工业相机二次开发

1.先下载海康的MVS客户端,下载安装后路径下会自动有相应的安装包,包括头文件以及lib库文件 海康机器人-机器视觉-下载中心 2.在pro项目文件里面配置库文件以及头文件 INCLUDEPATH $$PWD/Hik_Includes DEPENDPATH $$PWD/Hik_Includes LIBS -L$$PWD/L…

2026/7/16 19:24:41阅读更多 →
AI直接“上手“数据库:电科金仓KES MCP Server落地实操

AI直接“上手“数据库:电科金仓KES MCP Server落地实操

“orders这张表上都有哪些字段?索引是怎么建的?” “帮我看看这条SQL慢在哪里。” “如果给user_id和status加个联合索引,执行计划会变吗?” 这些过去要让DBA或开发人员来回切工具、复制粘贴才能回答的问题,如今在TRAE…

2026/7/16 19:24:40阅读更多 →
【JAVA毕设源码分享】基于Java的停车场管理系统的设计与实现(程序+文档+代码讲解+一条龙定制)

【JAVA毕设源码分享】基于Java的停车场管理系统的设计与实现(程序+文档+代码讲解+一条龙定制)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

2026/7/16 19:24:40阅读更多 →
【JAVA毕设源码分享】基于JavaWeb的点餐系统的设计与实现(程序+文档+代码讲解+一条龙定制)

【JAVA毕设源码分享】基于JavaWeb的点餐系统的设计与实现(程序+文档+代码讲解+一条龙定制)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

2026/7/16 19:24:40阅读更多 →
技术型创始人的市场感知框架:从趋势信号到商业机会的系统化转化

技术型创始人的市场感知框架:从趋势信号到商业机会的系统化转化

技术型创始人的市场感知框架:从趋势信号到商业机会的系统化转化 一、技术直觉的两面性:为什么技术型创始人容易错判市场 技术出身的创始人,最危险的时刻不是代码写不出来,而是用技术视角替代市场判断。一个典型的决策模式是&#…

2026/7/16 19:19:40阅读更多 →
VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异

VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异

VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异当你在VSCode中启动一个新的TypeScript项目时,第一个技术决策往往从安装方式开始。这个看似简单的选择——全局安装还是项目本地安装——实际上会深刻影响你的开发流程、团队协作和…

2026/7/16 8:28:11阅读更多 →
智慧树刷课插件:5分钟实现自动化学习的智能助手

智慧树刷课插件:5分钟实现自动化学习的智能助手

智慧树刷课插件:5分钟实现自动化学习的智能助手 【免费下载链接】zhihuishu 智慧树刷课插件,自动播放下一集、1.5倍速度、无声 项目地址: https://gitcode.com/gh_mirrors/zh/zhihuishu 智慧树刷课插件是一款专为智慧树在线教育平台设计的Chrome浏…

2026/7/16 6:53:04阅读更多 →
Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案

Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案

Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案 【免费下载链接】WorkshopDL WorkshopDL - The Best Steam Workshop Downloader 项目地址: https://gitcode.com/gh_mirrors/wo/WorkshopDL 你是否在GOG或Epic Games Store购买了心仪的游戏…

2026/7/16 12:02:41阅读更多 →
A--10 Codex Review与GitHub PR工作流实战指南:从代码审查到安全合并

A--10 Codex Review与GitHub PR工作流实战指南:从代码审查到安全合并

摘要:本文系统讲解如何利用Codex App的Review功能与GitHub PR工作流,实现从代码修改到安全合并的完整流程。涵盖Review面板深度使用、/review命令实战、GitHub Connector配置、PR描述撰写技巧,以及常见问题排查方法。通过多个实战案例和流程图,帮助开发者建立高效的AI辅助代…

2026/7/16 0:00:38阅读更多 →
遗传算法解5皇后问题:从Hello World到工业优化的进化实验室

遗传算法解5皇后问题:从Hello World到工业优化的进化实验室

1. 项目概述:为什么用遗传算法解5皇后问题,而不是直接回溯?我带过十几届算法课,也给不少初创团队做过AI架构咨询。每次讲到组合优化问题,学生和工程师的第一反应永远是“写个回溯试试”。这没错——55棋盘上找所有合法…

2026/7/16 0:00:38阅读更多 →
5.1V稳压管输出为何只有4.7V?工作电流与负载影响分析

5.1V稳压管输出为何只有4.7V?工作电流与负载影响分析

前几天调试一个简单的电源模块,用到了5.1V稳压管。电路接好,上电测试,万用表一量——输出居然只有4.7V。第一反应是稳压管坏了,换了一个新的,结果还是4.7V。这让我想起很多初学者都会遇到的困惑:明明标称5.…

2026/7/16 0:00:38阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/15 15:50:47阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/16 8:58:42阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/16 17:10:26阅读更多 →