在try catch块以及finally中return的位置直接影响程序执行的结果,我以返回值的类型和return出现的位置分类对各个情况的执行结果进行说明。
先上结论:
- 当返回类型为基本数据类型时:
(1) finally中没有return,返回值就是return处的值。eg:
public static void main(String[] args) {
System.out.println(testPrimitive());
}
public static int testPrimitive() {
int x = 0;
try {
x = 1;
return x;
} catch (Exception e) {
x = 2;
return x;
} finally {
x = 3; //基本类型的返回值,不可被修改
// return x; //可以被“具体值”覆盖
} }
输出 1
可能有人会想说finally下面有return呢?其实finally下面有return的作用等效于在try块里面和catch块里面加上return,作用是一样的,把try块里面的return和catch里面的return注释掉,然后在finally外面加上return,是一回事。所以,finally外面有return,结果还是1.
原因:之所以出现这种原因经过查看class文件(可以通过dos命令查看class文件: javap -verbose className
或者javap - c className
) 可以了解在try的return之后会将x变量的值赋给新的变量x’,然后最终将x’返回,所以fianlly怎么操作x都不影响x‘的值。
(2) finally中有return,首先要说明,正常是不建议在finally块里面写return语句的,原因就是因为
a: 可能会覆盖正常的流程返回的值造成代码难以理解,
b:如果finally块中包含了return语句,如果try块中抛出异常即使在catch块重新抛出了异常,则调用该方法的语句也不会获得catch块重新抛出的异常,由于最终会执行finally块,如果在finally中有返回return,n那会得到finally块的返回值,并且不会捕获异常。异常就被“吃”掉了。
但是如果写了,正常也是可以编译过不报错的,所以这里进行一下具体的分析:
eg:
public static void main(String[] args) {
System.out.println(test());;
}
static int test()
{
int x = 2;
try
{
x++;
// int i = 1/0;
return x;
}catch(Exception e){
x++;
// int i = 1/0;
return x;
}finally
{
//try {
// int i = 1/0;
++x;
return x;
}
}
输出 3.
原因
这里给出一个示例,通过查看calss字节码文件可知,在执行try块的时候在返回之前直接goto到了finally,try块中的return x并没有执行到。返回的仍旧是变量x.
以上是基本类型的,那接下来再看看引用类型的。
(1)finally中没有return
public static void main(String[] args) {
System.out.println(test());
//System.out.println(testPrimitive());
}
public static Student test() { //对象的地址值没有变
Student result = new Student("Tom", 0);
try {
result.setAge(1);
return result;
} catch (Exception e) {
result.setAge(2);
return result;
} finally {
result.setAge(3); //引用类型的返回值,可被修改
//return new Student("Kobe", 33); //可以被“具体值”覆盖
}
// return new Student("Kobe", 33);
}
输出:Tom 3
原因:这个很容易理解,因为java中应用类型时址传递,因此finally中其实是对new Student(“Tom”, 0);这个对象的地址操作,看字节码:
(2)finally中有return
public static Student test() { //对象的地址值没有变
Student result = new Student("Tom", 0);
try {
result.setAge(1);
return result;
} catch (Exception e) {
result.setAge(2);
return result;
} finally {
// result.setAge(3); //引用类型的返回值,可被修改
// return result;
return new Student("Kobe", 33); //可以被“具体值”覆盖
}
// return new Student("Kobe", 33);
}
同样字节码中同样goto了,看图:
所以这里最后输出Kobe 33
再贴一个例子大家看看:
public static void main(String[] args) {
System.out.println(getMap().get("KEY").toString());
}
public static Map<String, String> getMap() {
Map<String, String> map = new HashMap<String, String>();
map.put("KEY", "INIT");
try {
map.put("KEY", "TRY");
// System.out.println(map + "111111");
return map;
}
catch (Exception e) {
map.put("KEY", "CATCH");
return map;
}
finally {
map.put("KEY", "FINALLY");
// System.out.println(map + "222222");
map = null;
// System.out.println(map + "333333");
// return map;
}
// System.out.println(map + "444444");
// return map;
}
刚开始我一直以为会是空指针,因为按照字节码在try块的return处拷贝了一个变量出来,但是在finally处已经将map这个对象置空了,所以应该拷贝的对象也是空,但是最后输出结果是FINALLY后来发现自己一直有一个知识点错误:
看下例:
public static void main(String[] args) {
// System.out.println(test());
//System.out.println(testPrimitive());
Student result = new Student("Tom", 0);
System.out.println(result);
Student s = result;
//s.setAge(20);
//result.setName("Jerry");
System.out.println(s);
result = null;
//s.setAge(10);
System.out.println("--------------");
System.out.println(result);
System.out.println(s);
}
看输出:
com.csdn.ReturnAndFinallyTest$Student@179935d
com.csdn.ReturnAndFinallyTest$Student@179935d
--------------
null
com.csdn.ReturnAndFinallyTest$Student@179935d
我最初以为result置空之后s应该也是空,但是实际情况是s还是指向的是原对象,所以Student s = result;可以理解为将new Student(“Tom”, 0);这个对象再增加一个引用,s也指向这个对象,当result为nul时,s仍然不变。
类似的错误还有:
Clazz c1 = new Clazz();
c1.setP1("123);
System.out.println(c1.getP1()); //输出123
method(c1);
System.out.println(c1.getP1());//还是输出123,因为method方法中的c1是局部变量,方法执行完就弹栈了。如果没有c1 = new Clazz(); 那这里输出的就是234
void method(Clazz c1){
c1 = new Clazz();
c1.setP1("234");
};
以上。