朝花夕拾——finally/final/finalize拨云雾见青天

开发者福利!热门AI工具限时免费用 购周边即赠Coding Plan Lite,Claude Code、Cursor等20+工具畅享,效率翻倍! 阅读详情

Java编程中,经常会使用到异常处理,而finally看似的是try/catch后对逻辑处理的完善,其实里面却存在很多隐晦的陷阱;final常见于变量修饰,那么你在内部类中也见过吧;finalize作为GC回收对象前的一道门,什么时候执行,执行效果又是怎样,有时看看又忘了。下面是我总结网上朋友的一些博文及其帖子对三者进行总结。(重点讲下finally)

先看final

  • Final修饰变量不能被重新赋值,其修饰实例变量时,定义时指定值则可视为“宏变量”,在非静态代码块和构造器中初始化值则不是;其修饰类变量时,只有在定义时指定值才视为“宏变量”,在静态代码块中初始化则不是。
  • Final修饰的方法不能被重写
  • Final修饰的类不能被继承
  • 内部类一般使用final修饰的局部变量。

在看finalize

  • 系统调用finalize方法具有不确定性
  • finalize方法是protected方法,子类可以覆盖该方法以实现资源清理工作,GC在回收对象之前调用该方法。一般当对象在变成不可到达的时候,GC会判断是否覆盖该方法,如果没有覆盖就直接回收;如果覆盖就把该对象放进F-Queue队列并由一低优先级线程执行该对象的finalize方法,然后再次判断是否可达。
  • 尽量不要去调用finalize,如果调用,则避免对象再生,多用于关闭流。

最后细斟finally

  • Finall语句在return语句执行之后,return返回之前执行;
  • Finally块中的return语句执行结果覆盖try和catch中执行的return语句执行结果
  • Finally块中操作try/catch返回基本变量时,结果不受finally操作影响
  • Finally块中操作try/catch返回引用对象时。结果受finally操作影响
下面给出几个例子验证finally的4个重要观点

例子1代码如下:
package java_zhaohuaxishi;

public class Example1 {
		
	public int test(){
		int i = 0;		
		try{
			i = 10;
			System.out.println("try");
			return i+=10;
			
		}catch(Exception e){
			i = 20;
			return 200;
			
		}finally{
			System.out.println("finally");
			System.out.println(i);
		}
		
	}
		
	/**
	 * @param args
	 */
	public static void main(String[] args) {
				
		// TODO Auto-generated method stub
		System.out.println(new Example1().test());

	}

}
运行结果:
try
finally
20
20
这也意味着finally打印出来的是try中return已经计算了的,验证观点一。

例子2代码如下:
package java_zhaohuaxishi;

public class Example1 {
	
	
	public int test(){
		int i = 0;		
		try{
			i = 10;
			return 100;
			
		}catch(Exception e){
			i = 20;
			return 200;
			
		}finally{
			return i;
		}
		
	}
		
	/**
	 * @param args
	 */
	public static void main(String[] args) {
				
		// TODO Auto-generated method stub
		System.out.println(new Example1().test());

	}

}
上述时没有出现异常情况,打印如下:
10

再给出出现异常情况,代码如下:
package java_zhaohuaxishi;

public class Example2 {
		
	public int test(){
		int i = 0;		
		try{
			i = 10/0;
			return 100;
			
		}catch(Exception e){
			i = 20;
			return 200;
			
		}finally{
			
			return i;
		}
		
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
				
		// TODO Auto-generated method stub
		System.out.println(new Example2().test());

	}

}
打印结果如下:
20
结果是:无论出现异常与否,finally的return总会覆盖try/catch中return的结果,验证观点二。

例子3代码如下:
package java_zhaohuaxishi;

public class Example5 {
		
	public int test(){
		int i = 0;		
		try{
			i = 10;
			return i;			
		}catch(Exception e){
			i = 20;
			return i;			
		}finally{
			
			i=30;
			//return i;
		}		
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
				
		// TODO Auto-generated method stub
		System.out.println(new Example5().test());

	}
}
打印结果如下:
10
可以看到,实际上finally修改i变量是不起作用的,验证观点三。

例子4代码如下:
package java_zhaohuaxishi;

class Test{
	
	int num;
	
	public Test(int num){
		this.num = num;		
	}

	public int getNum() {
		return num;
	}

	public void setNum(int num) {
		this.num = num;
	}
		
}

public class Example3 {
	
	public Test test(){		
		Test t  = new Test(0);		
		try{
			t.setNum(10);
			
			return t;
			
		}catch(Exception e){
			t.setNum(20);
			return t;
			
		}finally{
			
			t.setNum(30);
		}
		
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println(new Example3().test().getNum());

	}

}
打印结果如下:
30
从上述结果来看,finally操作的对象确实是与try上的同一个对象;那么我们比较上面观点三,操作变量的时候是不能更改的,想想有点诡异,我们看下面代码:
package java_zhaohuaxishi;

class Test{
	
	int num;	
	public Test(int num){
		this.num = num;		
	}

	public int getNum() {
		return num;
	}

	public void setNum(int num) {
		this.num = num;
	}
	
	
}

public class Example3 {
	
	public int test(){
		
		Test t  = new Test(0);
		
		try{
			t.setNum(10);
			System.out.println(t.getNum());
			return t.getNum();
			
		}catch(Exception e){
			t.setNum(20);
			return t.getNum();
			
		}finally{
			System.out.println(t.getNum());
			t.setNum(30);
		}
		
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println(new Example3().test());

	}

}
这次我们不在返回对象,而是返回一个int变量。从观点一我们可以知道,返回的t.getNum()实际上是会先计算再保存起来。那么如果我们在finally中在去改变t的num,实际上t的num会被改变,然而返回的应该还是10!
打印结果如下:
10
30
10
果然!这与我们预先的是一模一样。如果有人觉得finally中t对象可能与try中不一致,下面例子将会让你觉得很神奇:
package java_zhaohuaxishi;

class Test4{
	
	int num;
	
	public Test4(int num){
		this.num = num;		
	}

	public int getNum() {
		return num;
	}

	public void setNum(int num) {
		this.num = num;
	}
	
	
}

public class Example4 {
	
	public Test4 test(Test4 t){		
		
		System.out.println("传进去的t:"+t.hashCode());
		
		try{
			t.setNum(10);
			
			return t;
			
		}catch(Exception e){
			t.setNum(20);
			return t;
			
		}finally{
			//t.setNum(30);
			System.out.println("finally修改前:"+t.hashCode());
			t = new Test4(0);
			System.out.println("finally修改后:"+t.hashCode());
			//return t;
		}
		
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Test4 t  = new Test4(0);
		System.out.println("return返回对象"+new Example4().test(t).hashCode());
		System.out.println("最后的t:"+t.hashCode());

	}

}
我们来看看打印结果:
传进去的t:2004703190
finally修改前:2004703190
finally修改后:1175576547
return返回对象2004703190
最后的t:2004703190
这结果看起来很神奇。我们验证观点四的时候操作对象是起作用的!然而当我们试图去修改t引用让他指向其他对象的时候竟然无效......

如有更多兴趣深入了解,可进一步认识JVM工作机制!



















从单例到依赖注入 这就是教科书式的 Service Locator:一个全局的、按类型查表的服务容器,谁需要谁自己去捞。就只依赖它真正用到的能力(接口隔离 ISP),依赖指向抽象(依赖倒置 DIP),而且你想塞个假的。——都从同一个字典里按类型捞实例,性能一样,对象也一样。判断该不该注入,别去问"能不能注入",要问的是"这个依赖需要被显性化和抽象化吗"。它有它的优点,这我不否认——简单、不用管线、随处可用。最理想的那种"初始化时注入",当然是构造函数注入。执行的时候,所有系统都注册完了,彼此拿引用是安全的。 阅读详情

相关推荐

基于Python批量获取过去某地某时的天气数据

此段代码为经Visual Weather API官网样例后修改。原代码返回数据格式为json,经测试json风速字段不可用,后改为返回数据格式为csv。若修改weather_api_endpoint,还可获取该地点的预测数据,但返回数据字段可能发生变化,请以官网文档为准。获取到的字段包括:平均温度、露点、湿度、风速、降雨量、能度、云量、天气类型等。目的:已知某一地址,获取过去某一时间该地的天气数据,并能够批量运行。这一步也会有获取不到的时间和地点,因此为空白。此段代码为经百度地图api官网样例后修改。

wiseman1233的博客 889

朝花夕拾——this和super到底是引用还是对象?

Java构造器在初始化的构成中经常会使用到this和super关键词,那么他们到底是特定类的引用还是对象呢?下面我们逐步看清他们的真实面目。 先看例子1——这个this到底是什么类型,又是谁的对象? package java_zhaohuaxishi; class Father{ private String name = "父亲"; public Father(){

「Android之禅」公众号博主,和你分享有价值有思考的技术文章 3408

MATLAB实战:用Costas环和Gardner环搞定QPSK同步问题(附完整代码)

本文详细介绍了如何使用MATLAB实现Costas环和Gardner环联合解决QPSK同步问题,包括载波同步和定时同步的技术原理、MATLAB代码实现及性能分析。通过完整的仿真示例和工程实践建议,帮助读者掌握QPSK同步技术的核心要点,提升数字通信系统的解调性能。

agile9scrum的博客 671

朝花夕拾——再看java类变量的初始化内幕

前面理解过java实例变量的初始化规则 朝花夕拾——看清java实例变量的初始化内幕 今天继续理清类变量的初始化顺序和细节,需要的朋友可以一起温习下。 类变量的初始化和实例变量的初始化很相似,相比实例变量初始化,少了构造器初始化的情况。大致只有:定义类变量时初始化和静态块中初始化两种情况。 规则:上述两种情况下,先对所有类变量进行申明并申请内存,在把赋值操作全部移动到静态块中进行初始化,顺序

「Android之禅」公众号博主,和你分享有价值有思考的技术文章 2139

朝花夕拾——你了解构造器初始化顺序吗?

java程序中,创建java对象,其初始化顺序总是先调用最顶层父类的初始化操作,包括初始化块和构造器,然后再向下调用其他类的初始化操作。 类和实例的初始化顺序可以参考 朝花夕拾——看清java实例变量的初始化内幕 朝花夕拾——再看java类变量的初始化内幕 先看看代码例子 package java_zhaohuaxishi; class GrandFather{ {

「Android之禅」公众号博主,和你分享有价值有思考的技术文章 2099

朝花夕拾——看清java实例变量的初始化内幕

过一段时间可能就得去找实习,今天在宿舍翻开以前常放在枕头边的大砖头《Java编程思想》打算重温下之前学的知识点,看到有关java初始化的一些知识,综合之前看过的网页和书籍,记下自己对java实例变量初始化的理解,仅供需要的朋友参考,也为自己积累一些java编程应该注意的知识点。     java中对实例变量的初始化可以在三个地方出现:定义实例变量时指定初始值;初始化块中为实例变量指定的初始值;构

「Android之禅」公众号博主,和你分享有价值有思考的技术文章 1968

解决 Activiti UEL 表达式解析异常 TreeBuilderException

在Activiti工作流中,排他/包容网关条件表达式因嵌套${}导致UEL解析失败,抛出TreeBuilderException。根源在于UEL语法禁止表达式内再次使用${},且需避免XML特殊字符干扰。解决方案包括:移除内部定界符、使用CDATA包裹表达式、确保返回布尔值。建议通过正则扫描、IDE插件校验、编码规范(如单引号字符串、复杂逻辑外置)及测试验证,从源头预防此类问题。

小李同学的博客 504

JVM 垃圾回收机制详解:从标记过程、回收算法到垃圾收集器

本文系统梳理 JVM 垃圾回收机制,讲解堆内存分区、GC Roots 可达性分析,详解标记‑清除、复制、标记‑整理三大基础回收算法,对比 Serial、Parallel、CMS、G1、ZGC 多款垃圾收集器,分析各自优缺点与适用场景,帮助开发者吃透 GC 底层原理,理解内存管理,提升 Java 性能调优能力。

加油,量变引起质变 463

Java EE 进阶:3.SpringBoot 快速上手(第一弹):配置环境

Java EE 进阶:3.SpringBoot 快速上手(第一弹):配置环境

要努力去发光,而不是被照亮~ 298

基于Spring AI 的智能聊天机器人

该项目使用最新的Spring AI 2.0.x ,从零开始搭建智能体;文章提到有些代码可使用多种方式书写,这些不同的书写方式都是从Spring AI官方得来,自己也实践了。

Qq121363Aa的博客 165

[071][验证码模块]基于Spring拦截器的验证码认证设计思想

本文介绍基于Spring拦截器的验证码认证设计,通过@CaptchaAuth注解与CaptchaAuthHandlerInterceptor实现声明式验证码校验。结合工厂模式支持多类型验证码(图形、短信等),解耦业务逻辑,提升复用性与可扩展性。方案遵循单一职责与开闭原则,无侵入式集成Spring生态,配合全局异常处理器统一响应错误,适用于登录、支付等高安全场景。

杨运交的博客 370

jquick-pdf 核心原理解析:基于 HTML 模板动态渲染 PDF 的实现逻辑

六层链路是排错地图:读入、解析、变量绑定、布局与分页、渲染输出、结果返回,异常先分层再定位。二分删减是最实用的定位手段:先保留标题与一段文字,再逐项恢复表格、绑定值与分页节点。显式分页与自然分页必须分开回归;只是分页提示,不是绝对约束。适用边界与常误区:jquick-pdf 位于“模板抽象”与“纯 Java 部署”之间,适合规则明确的业务文档,不适合依赖脚本与完整浏览器布局的页面;它与 iText、PdfBox 不是替代关系,后两者在底层控制上更强。

PaoHaiJiao的博客 562

Spring Boot 多数据源实战:让不同 Mapper 走不同数据源

适合需要运行时动态切换数据源的场景,比如读写分离、按租户切换。让不同 Mapper 走不同数据源,核心是让每个 Mapper 绑定到独立的 SqlSessionFactory。如果业务库之间完全独立、互不切换,推荐方案一,简单直观;如果需要运行时动态切换(如读写分离),推荐方案二。实际项目中也可以两者结合:静态分包 + 动态路由兜底。

KevinChen2019的博客 245

GitLab+Jenkins CI-CD自动化部署实战

本文记录了团队基于GitLab与Jenkins搭建CI/CD流水线的实战过程。通过制定Git Flow分支规范(如master/develop保护分支、MR审核机制),实现代码质量管控;利用Jenkins配置Git拉取、Maven构建、SSH部署及邮件通知,完成“推送即构建、构建即部署”的自动化流程。针对开发与生产环境采用不同触发策略(自动/手动),并集成备份与回滚脚本保障发布安全。全文涵盖环境搭建、插件配置、常坑点及面试高频问题,为DevOps实践提供完整参考。

2301_78547133的博客 343

基于springboot+vue的学生信息管理系统的设计与实现 源码+文档

本文分享基于SpringBoot3+Vue3的高校选课管理系统项目,涵盖管理员、教师、学生多角色功能,实现课程管理、选课审批、成绩录入、考勤登记及通知公告等。技术栈为Vue3、ElementPlus、ECharts、SpringBoot3、MyBatis-Plus、SpringSecurity与MySQL。提供源码、数据库、万字文档、答辩PPT及部署指导,支持定制开发与持续技术答疑。获取方式置顶链接(点我进入)。

大学生毕设选题 / 源码文档 → 置顶文章名片交流 272

de风——【从零开始学C++】(二十)红黑树封装map和set

本文从零实现C++中set与map的封装,核心在于利用泛型与红黑树共用底层结构。通过KeyOfT仿函数统一比较逻辑,确保仅按key排序;借助const修饰使key不可修改,保障数据结构有序性;结合迭代器实现范围遍历,并通过Insert返回pair<iterator, bool>支持map的operator[]功能。最终完成mymap与myset的完整封装,揭示其本质为“红黑树+泛型外壳”,帮助新手彻底理解容器底层机制。

Xiao_running的博客 613

Java 编译、JDK 与 Android 构建入口

Android 项目中的 Java 源码先由 JDK 的 javac 编译成 class,AGP 在 Gradle 任务图里组织后续 Android 构建工作,D8 把字节码转成 dex 并打进 APK。安装后系统启动应用进程,ART 通过类加载器解析 APK 中的 dex,ActivityThread 在绑定应用时经 LoadedApk 和 Instrumentation 创建 Application。所以 Gradle 配置属于构建期,Application.onCreate() 属于运行期;R8 是否

trebleZ的博客 457

# Java文件打包成可执行JAR包(两种方式:原生javac\+jar命令 / Maven)

否则报错)

金玉满堂的博客 328

Collections常用方法(速查笔记)

Java Collections 是 java.util 包中的核心工具类,掌握高频用法可高效应对开发需求。常用包括:创建不可变集合(如 singletonList、emptySet)、包装只读或线程安全集合(如 unmodifiableList、synchronizedMap),以及排序、反转、打乱、二分查找等操作。注意:sort、reverse 等方法会修改原列表,使用时需谨慎,必要时先拷贝。建议结合官方 Javadoc 查阅方法摘要,提升编码效率与安全性。

qq_38980678的博客 352
上一篇: 朝花夕拾——this和super到底是引用还是对象?
下一篇: Https(SSL/TLS)原理详解
Android之禅
博客等级 码龄13年 135粉丝 78原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值