朝花夕拾——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工作机制!



















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值