这两天在看异常处理,书上的看不动所以找了一篇博客。(https://blog.youkuaiyun.com/yongyuai/article/details/79752608)
虽然很长但还是看完了。最后发现了一个问题(其实是看到评论里说了才发现的)。
无论try中的代码是否会抛出异常,也就是无论catch代码块中的代码是否会被执行,最后都会执行finally中的代码。并且如果在try/catch中发现了return语句,此时在return语句执行之前会跳转到finally中,先执行完finally中的代码再返回return语句返回值。但在运行程序时发现了一些不同:
public class TestOfFinally{
public static void main(String[] arys){
System.out.println(re());
}
static int re(){
int i=0;
try{
return i;
}finally{
i=2;
System.out.println("finally "+i);
}
}
}
当我们执行以上代码时会得到以下输出:
C:\Users\ye142\Desktop\j>java TestOfFinally
finally 1
0
此时我们可以看到,finally确实先于return语句被执行(因为输出了“finally”语句),但在输出语句上的“i=2;“语句似乎被忽略了。程序仍然返回了声明i时的i=0的值。
如果我们将此处的基本数据类型更换为实例变量,
public class TestOfFinally{
public static void main(String[] arys){
System.out.println(re());
System.out.println(ret());
}
static int re(){
int i=0;
try{
return i;
}finally{
i=2;
System.out.println("finally "+1);
}
}
static int ret(){
object ob=new object();
try{
ob.i=0;
return ob.i;
}finally{
ob.i=2;
System.out.println("finally "+ob.i);
}
}
}
class object{
int i;
}
会得到以下结果:
C:\Users\ye142\Desktop\j>java TestOfFinally
finally 1
0
finally 2
0
根据我们查到的博客,如果return的是一个基本数据类型,finally中的修改并不会改变最终的返回值,如果return的只一个引用变量,则会改变返回结果,因为return语句会预存变量值,对于基本数据类型,return会直接储存数据的值,而对于引用变量,因为return中保存的是一个内存地址,并且该地址与finally中修改的值的内存地址相同,所以最终返回值仍会被改变。但是我们上面的结果与此并不相符。
此时我猜想上述代码中的”ob.i“仍然是一个具体的值,return预存的仍然是具体值而不是内存地址。我们可以使用一个函数来验证我们的猜想:
public class TestOfInstance{
public static void main(String[] arys){
object o=new object();
System.out.println(o.data);
changeData(o.data);
System.out.println(o.data);
changeObject(o);
System.out.println(o.data);
}
static void changeData(int data){
data=10;
}
static void changeObject(object ob){
ob.data=100;
}
}
class object{
int data=0;
void setData(int data){
this.data=data;
}
}
最后得到以下结果:
C:\Users\ye142\Desktop\j>java TestOfInstance
0
0
100
我们的猜想得到了最基本的验证(看不懂字节码,还不能完全验证)。
现在根据我们的猜想更改代码:
public class TestOfFinally{
public static void main(String[] arys){
System.out.println(re());
System.out.println(ret());
System.out.println(retu().i);
}
static int re(){
int i=0;
try{
return i;
}finally{
i=2;
System.out.println("finally "+1);
}
}
static int ret(){
object ob=new object();
try{
ob.i=0;
return ob.i;
}finally{
ob.i=2;
System.out.println("finally "+ob.i);
}
}
static object retu(){
object ob=new object();
try{
ob.i=0;
return ob;
}finally{
ob.i=2;
System.out.println("finally "+ob.i);
}
}
}
class object{
int i;
}
结果如下:
C:\Users\ye142\Desktop\j>java TestOfFinally
finally 1
0
finally 2
0
finally 2
2
此时我们发现return返回值被finally中的语句改变!
现在我们可以解释上面各种结果出现的原因了:
当try/catch中遇到return语句时,return语句并不执行(如果return后跟的语句仍然会执行,如"return --i",可使用该语句验证),而是先创建return值的副本,然后跳转到finally代码块中,执行完finally中的语句后回到try/catch中执行return语句(如果finally中存在return语句则不会回到try/catch的return语句)。如果return后是一个基本数据类型,则创建的副本为该值,若return中是一个对象,则副本为该对象的内存地址,在finally中改变值后会在return中生效。