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

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工作机制!

时间: 2024-08-29 13:17:26

朝花夕拾——finally/final/finalize拨云雾见青天的相关文章

final finalize finally的区别

final修饰的类不可以被继承: final修饰的方法不可以被覆盖: final修饰成员变量,经常和static连用,表示静态常量,要大写: final 修饰的的局部变量,必须显示的初始化,只能赋值一次. finalize 是在GC执行之前,可以执行的一个方法. finally和try....catch...连用表示必须执行的代码.但是在执行finally语句块之前退出JVM,finally语句块就不会执行.

java异常面试常见题目

在Java核心知识的面试中,你总能碰到关于 处理Exception和Error的面试题.Exception处理是Java应用开发中一个非常重要的方面,也是编写强健而稳定的Java程序的关键,这自然使它成为面试题中的常客.关于Java中Error和Exception的面试题目多是关于Exception和Error的概念,如何处理Exception,以及 处理Exception时需要遵守的最佳实践等等.尽管关于多线程.垃圾回收.JVM概念和面向对象设计等方面的问题依然主宰着这类面试,你仍然需要为回答

JAVA面试必备的知识宝典(二)

WeakReference与SoftReference的区别? 这点在四种引用类型中已经做了解释,这里简单说明一下即可: 虽然 WeakReference 与 SoftReference 都有利于提高 GC 和 内存的效率,但是 WeakReference ,一旦失去最后一个强引用,就会被 GC 回收,而软引用虽然不能阻止被回收,但是可以延迟到 JVM 内存不足的时候. 为什么要有不同的引用类型 不像C语言,我们可以控制内存的申请和释放,在Java中有时候我们需要适当的控制对象被回收的时机,因此

(转) 很牛的求职经历

工作刚刚落实,论文也刚完成,终于有时间对自己的求职历程及求职经验进行总结了.应同学要求,最近准备书写系列文章“我的求职历程及经验分享”,请大家关注,谢谢! (一):求职历程总结 2007 年1月10日,随着在三方协议上郑重签下自己的名字,我的求职历程终于划上了一个圆满的句号.在这三个月漫长而艰辛的过程中,我付出了很多,经历了很多, 也收获了很多.这一路走来,要感谢的人太多太多,尤其要感谢每一个在找工作过程中关心.帮助过我的人,我会一生铭记你们的恩情.现在,尘埃落定,到了该回 报的时候,我首先要回

Java面试宝典

http://www.cnblogs.com/bluestorm/p/6429894.html Java面试宝典 面向对象的三个特征 封装,继承,多态.这个应该是人人皆知.有时候也会加上抽象. 多态的好处 允许不同类对象对同一消息做出响应,即同一消息可以根据发送对象的不同而采用多种不同的行为方式(发送消息就是函数调用).主要有以下优点: 可替换性:多态对已存在代码具有可替换性. 可扩充性:增加新的子类不影响已经存在的类结构. 接口性:多态是超累通过方法签名,想子类提供一个公共接口,由子类来完善或

Java的Exception和Error面试题10问10答

在Java核心知识的面试中,你总能碰到关于 处理Exception和Error的面试题.Exception处理是Java应用开发中一个非常重要的方面,也是编写强健而稳定的Java程序的关键,这自然使它成为面试题中的常客.关于Java中Error和Exception的面试题目多是关于Exception和Error的概念,如何处理Exception,以及 处理Exception时需要遵守的最佳实践等等.尽管关于多线程.垃圾回收.JVM概念和面向对象设计等方面的问题依然主宰着这类面试,你仍然需要为回答

计算机类研究生求职经历以及经验共享(转)

计算机类研究生求职经历以及经验共享(转) 2007-08-10 12:34 一.      求职历程总结 2007年1月10日,随着在三方协议上郑重签下自己的名字,我的求职历程终于划上了一 个圆满的句号.在这三个月漫长而艰辛的过程中,我付出了很多,经历了很多,也收获了很 多.这一路走来,要感谢的人太多太多,尤其要感谢每一个在找工作过程中关心.帮助过我 的人,我会一生铭记你们的恩情.现在,尘埃落定,到了该回报的时候,我首先要回报的就 是我的母校和同学.所以,决定把自己找工作中积累的经验写出来给大家

Java 面试总结 面试常问的关键字总结

文章出处http://www.cnblogs.com/IUbanana/p/7116520.html 关键字: final finalize finally throws和throw static关键字的作用 abstract 和 interface super 和 this synchronize 和 volatile 1. final finalize finally对比 (1)性质不同 final为关键字: finalize()为方法: finally为为区块标志,用于try语句中: (2

J2SE基础:11.异常处理

1:异常的概念: 异常是程序在执行时发生的事件(异常发生在运行期间). 程序出现错误,打断原本的执行流程. 2:Java中处理异常. 在Java中,异常被封装成一个对象.(属性和方法) 3:异常产生 在Java程序的执行过程中,如果出现了异常事件,就会生成一个异常对象. 生成的异常对象将传递给Java运行时系统,这一异常的产生和提交过程称 为引发异常. 4:异常的分类 异常的根类(throwable) 错误:Error(程序无法处理的错误) 异常:Exception(可以处理的错误.程序可以捕获