在finally代码块中处理返回值,这是在面试题中经常出现的题目.但是在项目中绝对不能再finally代码块中出现return语句,这是因为这种处理方式非常容易产生"误解",会严重误导开发者.

 public class Client {
public static void main(String[] args) {
try {
doStuff(-1);
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方法的返回值是什么?

②duStuff方法永远都不会抛出异常吗?

答案是:doStuff(-1)的值是-1. doStuff(100)的值也是-1.调用doStuff方法永远不会抛出异常,原因就在于我们再finally代码块中加入了return语句.而这会导致出现两个问题:

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

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

是不是可以定义一个变量,在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代码块就标志着运行时会有一个Throwable线程监视着该方法的运行,若出现异常,则交给异常逻辑处理.

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

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

 public class Client {

     public static void main(String[] args) {
System.out.println(doStuff2().getName());
} public static Student doStuff2() {
Student person = new Student();
person.setName("张三");
try {
return person;
} catch (Exception e) {
} finally {
//重新修改一下返回值
person.setName("李四");
}
person.setName("王五");
return person;
}
} class Student{
private String name; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} }

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

(2)屏蔽异常

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

 public class Client {

     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方法就不能获得任何的异常信息了,这样的代码会使得可读性大大降低,读者很难理解作者意图,增加了修改难度.

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

与return相似,System.exit(0)或Runtime.getRuntime().exit(0)出现在异常代码块中也会产生非常多类似的错误假象....

[改善Java代码]不要在finally块中处理返回值的更多相关文章

  1. try--catch--finally中return返回值执行的顺序(区别)

    1.try块中没有抛出异常,try.catch和finally块中都有return语句 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public static int ...

  2. java通过jdbc访问mysql,update数据返回值的思考

    java通过jdbc访问mysql,update数据返回值的思考 先不说那么多,把Java代码贴出来吧. public static void main(String[] args) throws I ...

  3. try--catch--finally中return返回值执行的顺序

    1.try块中没有抛出异常,try.catch和finally块中都有return语句 public static int NoException(){ int i=10; try{ System.o ...

  4. Java程序调用带参数的shell脚本返回值

    Java程序调用带参数的shell脚本返回值 首先来看看linux中shell变量(\(#,\)@,$0,$1,\(2)的含义解释 变量说明: -  \)$  Shell本身的PID(ProcessI ...

  5. Controller 中Action 返回值类型 及其 页面跳转的用法

        •Controller 中Action 返回值类型 View – 返回  ViewResult,相当于返回一个View 页面. -------------------------------- ...

  6. Web API中的返回值类型

    WebApi中的返回值类型大致可分为四种: Void/ IHttpActionResult/ HttpResponseMessage /自定义类型 一.Void void申明方法没有返回值,执行成功后 ...

  7. Asp.net MVC 中Controller返回值类型ActionResult

    [Asp.net MVC中Controller返回值类型] 在mvc中所有的controller类都必须使用"Controller"后缀来命名并且对Action也有一定的要求: 必 ...

  8. robot framework中的返回值

    1.若想要再setup中有返回值,给后续的操作使用 A)在setup的关键词中需要的返回值,设置为global variable或者suit variable:如下图:但是在编译器中,会报错,但是执行 ...

  9. c++中带返回值函数没写return能通过编译但运行时会出现奇怪问题

    c++中带返回值函数没写return能通过编译但运行时会出现奇怪问题 例如: string myFunc(){ theLogics(); } 发现调用: myFunc(); 崩溃. 但调用: cout ...

随机推荐

  1. homework-06

    围棋问题 关于代码的阅读,写注释,我的代码阅读量和阅读能力都有限,而且是关于没有基础的围棋问题,JAVA和C#混合的程序.不免参考了其他同学的思路,忘老师见谅. 1.playPrev(GoMove) ...

  2. 教程-在F9后提示内存错误,点击了乎略,之后怎么取消乎略?

    问题现象:F9后,调试程序,提示内存错误,点击了“乎略”.之后再也没有出现错误了.可是想改这个BUG时,没法取消乎略了. 问题原因:在DLEPHI的选项中是这么一个地方是可以设置的. 问题处理:打开D ...

  3. Red5点播和直播的实现

    (一)        Red5流媒体服务器介绍Red5是一个采用Java开发开源的Flash流媒体服务器.它支持:把音频(MP3)和视频(FLV)转换成播放流: 录制客户端播放流(只支持FLV):共享 ...

  4. HDU 4791 Alice's Print Service (2013长沙现场赛,二分)

    Alice's Print Service Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Ot ...

  5. properties.load(in); 引出的中文乱码问题

    /** * Reads a property list (key and element pairs) from the input byte stream. * The input stream i ...

  6. MEF 编程指南(十):重组

    有些应用程序被设计成在运行时动态地改变.例如,一个新的扩展可能被下载,或者其他原因变得不可用.MEF 依靠我们称之为重组(Composition)的技术处理,在初始化组合以后改变导入值的场景.   导 ...

  7. Hibernate中的session对象update方法的使用

    使一个游离对象转变为持久化对象.例如以下代码在session1中保存了一个Customer对象,然后在session2中更新这个Customer对象: Customer customer = new ...

  8. 配置文件struts2Struts2配置文件模块化包含(include)与action总结

    本文是一篇关于配置文件struts2的帖子 <include>标签 当Struts配置文件比较多,需要模块化分别或分开成为多个配置文件时,这个功能比较好. 则需要使用<include ...

  9. 我所经历的SAP选型

    这是一个失败的选型项目,而且在可遇见的未来公司也不会再经历SAP选型,甚至不会再启动erp项目,个中原因很难一言道尽,在此简要的说说我们的选型过程以及在选型过程中对各种因素的考虑. 一.重启选型工作七 ...

  10. 禁止Android的StatusBar下拉

    Android中有许多隐藏的Service,StatusBarManager就是其中一个,在Context.java中可以看到: /** * Use with {@link #getSystemSer ...