昆明java培训
达内昆明广州春城路

18487146383

热门课程

【昆明Java培训】不要在finally块中处理返回值

  • 时间:2016-10-19
  • 发布:昆明Java培训
  • 来源:达内新闻

昆明Java培训的老师知道,在finally代码块中处理返回值,这是考试和面试中经常出现的题目。虽然可以以此来出考试题,但在项目中绝对不能再finally代码块中出现return语句,这是因为这种处理方式非常容易产生"误解",会误导开发者。例如如下代码:

public class Client113 {

public static void main(String[] args) {

try {

System.out.println(doStuff(-1));

System.out.println(doStuff(100));

} catch (Exception e) {

System.out.println("这里是永远不会到达的");

}

}

//该方法抛出受检异常

public static int doStuff(int _p) throws Exception {

try {

if (_p < 0) {

throw new DataFormatException("数据格式错误");

} else {

return _p;

}

} catch (Exception e) {

//异常处理

throw e;

} finally {

return -1;

}

}

}

对于这段代码,有两个问题:main方法中的doStuff方法的返回值是什么?doStuff方法永远都不会抛出异常吗?

昆明Java培训的老师的答案是:doStuff(-1)的值是-1,doStuff(100)的值也是-1,调用doStuff方法永远都不会抛出异常,有这么神奇?原因就是我们在finally代码块中加入了return语句,而这会导致出现以下两个问题:

(1)、覆盖了try代码块中的return返回值

当执行doStuff(-1)时,doStuff方法产生了DataFormatException异常,catch块在捕捉此异常后直接抛出,之后代码执行到finally代码块,就会重置返回值,结果就是-1了。也就是出现先返回,再重置返回的情况。

有人可能会思考,是不是可以定义变量,在finally中修改后return呢?代码如下:

public static int doStuff() {

int a = 1;

try {

return a;

} catch (Exception e) {

} finally {

//重新修改一下返回值

a = -1;

}

return 0;

}

该方法的返回值永远是1,不会是-1或0(为什么不会执行到" return 0 "呢?原因是finally执行完毕后该方法已经有返回值了,后续代码就不会再执行了),这都是源于异常代码块的处理方式,在代码中try代码块就标志着运行时会有一个Throwale线程监视着该方法的运行,若出现异常,则交由异常逻辑处理。

昆明Java培训的老师知道方法是在栈内存中运行的,并且会按照“先进后出”的原则执行,main方法调用了doStuff方法,则main方法在下层,doStuff方法在上层,当doStuff方法执行完" return a "时,此方法的返回值已经确定int类型1(a变量的值,注意基本类型都是拷贝值,而不是引用),此时finally代码块再修改a的值已经与doStuff返回者没有任何关系了,因此该方法永远都会返回1.

继续追问,那是不是可以在finally代码块中修改引用类型的属性以达到修改返回值的效果呢?代码如下:

class Person {

private String name;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

}

public static Person doStuffw() {

Person person = new Person();

person.setName("张三");

try {

return person;

} catch (Exception e) {   

} finally {

//重新修改一下值

person.setName("李四");

}

person.setName("王五");

return person;

}

此方法的返回值永远都是name为李四的Person对象,原因是Person是一个引用对象,在try代码块中的返回值是Person对象的地址,finally中再修改那当然会是李四了。

(2)、屏蔽异常

为什么明明把异常throw出去了,但main方法却捕捉不到呢?这是因为异常线程在监视到有异常发生时,就会登记当前的异常类型为DataFormatException,但是当执行器执行finally代码块时,则会重新为doStuff方法赋值,也就是告诉调用者"该方法执行正确,没有产生异常,返回值为1 ",于是乎,异常神奇的消失了,其简化代码如下所示:

public static void doSomeThing(){

try{

//正常抛出异常

throw new RuntimeException();

}finally{

//告诉JVM:该方法正常返回

return;

}

}

public static void main(String[] args) {

try {

doSomeThing();

} catch (RuntimeException e) {

System.out.println("这里是永远不会到达的");

}

}

上面finally代码块中的return已经告诉JVM:doSomething方法正常执行结束,没有异常,所以main方法就不可能获得任何异常信息了。这样的代码会使可读性大大降低,读者很难理解作者的意图,增加了修改的难度。

在finally中处理return返回值,代码看上去很完美,都符合逻辑,但是执行起来就会产生逻辑错误,最重要的一点是finally是用来做异常的收尾处理的,一旦加上了return语句就会让程序的复杂度徒然上升,而且会产生一些隐蔽性非常高的错误。

与return语句相似,System.exit(0)或RunTime.getRunTime().exit(0)出现在异常代码块中也会产生非常多的错误假象,增加代码的复杂性,大家有兴趣可以自行研究一下。

上一篇:java中受检异常尽可能转化为非受检异常
下一篇:spring事务与消息队列

“因材施教,分级培优”十问十答

达内举办“2016授课讲师资格认证培训“,不断提升教学品质

达内牵手猿圈科技,打造技能测评、学习、就业一站式服务

毕业三年之内能转行学编程吗?

选择城市和中心
贵州省

广西省

海南省