zoukankan      html  css  js  c++  java
  • [改善Java代码]不要在finally块中处理返回值

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

     1 public class Client {
     2     public static void main(String[] args) {
     3         try {
     4             doStuff(-1);
     5             doStuff(100);
     6         } catch (Exception e) {
     7             System.out.println("这里是永远都不会到达的");
     8         }
     9     }
    10 
    11     public static int doStuff(int _p) throws Exception {
    12         try {
    13             if (_p < 0) {
    14                 throw new DataFormatException("数据格式错误");
    15             } else {
    16                 return _p;
    17             }
    18         } catch (Exception e) {
    19             //异常处理
    20             throw e;
    21         } finally {
    22             return -1;
    23         }
    24     }
    25 }

    对于以上代码,有两个问题:

    ①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代码块中修改引用类型的属性,以达到修改返回值的效果呢?代码如下:

     1 public class Client {
     2     
     3     public static void main(String[] args) {
     4         System.out.println(doStuff2().getName());
     5     }
     6     
     7     public static Student doStuff2() {
     8         Student person = new Student();
     9         person.setName("张三");
    10         try {
    11             return person;
    12         } catch (Exception e) {
    13         } finally {
    14             //重新修改一下返回值
    15             person.setName("李四");
    16         }
    17         person.setName("王五");
    18         return person;
    19     }
    20 }
    21 
    22 class Student{
    23     private String name;
    24 
    25     public String getName() {
    26         return name;
    27     }
    28 
    29     public void setName(String name) {
    30         this.name = name;
    31     }
    32     
    33 }

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

    (2)屏蔽异常

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

     1 public class Client {
     2 
     3     public static void doSomething() {
     4         try {
     5             //正常抛出异常
     6             throw new RuntimeException();
     7         } finally {
     8             //告诉JVM:该方法正常返回
     9             return;
    10         }
    11     }
    12     
    13     public static void main(String[] args) {
    14         try {
    15             doSomething();
    16         } catch (RuntimeException e) {
    17             System.out.println("这里永远都不会到达!");
    18         }
    19     }
    20 }

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

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

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

  • 相关阅读:
    Python字符串
    ListCtrl控件
    leetcode1004
    leetcode1003
    leetcode1002
    leetcode153
    leetcode540
    leetcode435
    leetcode999
    leetcode997
  • 原文地址:https://www.cnblogs.com/DreamDrive/p/5620914.html
Copyright © 2011-2022 走看看