zoukankan      html  css  js  c++  java
  • finally语句块

    finally语句块是搭配着try语句块出现的,也就说必须有try语句块才会有finally语句块,但是并不是try语句块都会搭配有finally语句块出现,我们常见的更多是try...catch...

    finally语句块一般出现的情况如下:

    public int operation() {   
           int result = 2016;   
                /*statements*/
           try {        
                /*statements*/
            } catch (Exception e) { 
                /*statements*/
            } finally {        
                /*statements*/
            }
    }
    

    一、finally语句块的执行时机

    • finally语句并不是每次都执行,只有线程执行过try语句块,finally语句块才会执行。如果线程执行到try语句块之前就return的话,finally语句是不会执行的。如例程中第一种情况,在try语句块之前执行return语句将会直接退出函数。
    • 只要线程进入过try语句块,不论有没有抛出异常,finally语句块都会执行。如例程中的第二种和第三种情况,线程执行进入try语句块,不管有没有出现异常,finally语句都正常执行。
    • finally 语句块是在结果返回之前执行的。根据第二种和第三种情况,可以发现finally语句执行在main函数打印结果之前。
    public class Test {
    
        public static void main(String[] args) {
            System.out.println("return before try");
            System.out.println("value: " + test(1));
            System.out.println("return after try");
            System.out.println("value: " + test(0));
            System.out.println("return after try catch");
            System.out.println("value: " + test());
        }
    
        public static int test(int i) {
            if (i == 1)
                return 0;
            try {
                System.out.println("try block");
                return i;
            } finally {
                System.out.println("finally block");
            }
        }
    
        public static int test(){
            int i = 1;
            try {
                System.out.println("try block");
                i = 1 / 0;
                return 1;
            }catch (Exception e){
                System.out.println("exception block");
                return 2;
            }finally {
                System.out.println("finally block");
            }
        }
    }
    

    执行结果

    return before try
    value: 0
    return after try
    try block
    finally block
    value: 0
    return after try catch
    try block
    exception block
    finally block
    value: 2
    

    二、finally语句块对返回结果的影响
    上面的例子为了不影响读者理解,在finally语句中没有做任何可能会影响到返回结果的操作。但是在finally语句块中对返回结果进行操作的话,会不会对返回结果造成影响就要分情况而论了。在学习接下来这部分知识之前,笔者是坚持认为finally语句对返回结果进行操作,是肯定会影响返回结果的值的,毕竟根据上面的理解,finally语句块执行在结果返回之前嘛,随着深入学习,笔者发现这里面还是有需要注意的地方的。先看一段例程:

    public class Test {
        public static void main(String[] args) {
            System.out.println("value : " + getValue());
        }
    
        public static int getValue() {
            int i = 1;
            try {
                return i;
            } finally {
                i++;
            }
        }
    }
    

    执行结果:

    value : 1
    

    按照之前的理解,这个程序执行得有点不按套路出牌啊,执行完try语句之后,程序先不返回,接着执行finally语句块,做自增操作,返回值应该是2啊,怎么会是1呢?我不知道各位读者这会什么心情,反正当时我看到这结果的时候,用一脸懵逼来形容是毫不为过。

    结果查阅资料,发现这个地方Java里面是有特殊处理的,下面这段话是抄的:
    “Java 虚拟机会把 finally 语句块作为 subroutine直接插入到 try 语句块或者 catch 语句块的控制转移语句之前。但是,还有另外一个不可忽视的因素,那就是在执行 subroutine之前,try 或者 catch 语句块会保留其返回值到本地变量表中。待 subroutine 执行完毕之后,再恢复保留的返回值到操作数栈中,然后通过 return 或者 throw 语句将其返回给该方法的调用者。”

    也就是说在try...catch...中执行到return语句的时候,会先把需要返回的数据缓存下来,再去执行finally语句块,执行完finally语句块之后,再将缓存的数据作为结果返回。这样看来上面例程的执行结果就说得过去了。是不是好开心,感觉一下子豁然开朗了呢。不要着急,咱们再入一个例程:

    public class Test {
    
        static class Monitor {
            int flag;
    
            public Monitor(int flag) {
                this.flag = flag;
            }
    
            public void setFlag(int flag) {
                this.flag = flag;
            }
    
            public int getFlag() {
                return this.flag;
            }
        }
    
        public static void main(String[] args) {
            Monitor monitor = getValue();
            System.out.println("flag : " + monitor.getFlag());
        }
    
        public static Monitor getValue() {
            Monitor monitor = new Monitor(0);
            try {
                return monitor;
            } finally {
                monitor.setFlag(1);
            }
        }
    }
    

    执行结果:

    flag : 1
    

    不是说在执行finally语句之前会把结果缓存的吗?为什么返回的结果会是1,应该是0才对的啊。其实这个地方出现疑问是因为不了解缓存到底存的是什么。这两个例程在我们的理解上产生冲突,是因为这两个例程中,finally语句块操作的是两种类型的变量,一个是基本类型,一个是对象类型。这两种类型的变量的存储方式是不一样的。int等基本类型的变量,在变量对应的内存中直接存放的就是该变量的值,而对象类型的变量,在其对应的内存中存放的是对象所在的地址。执行finally语句之前,Java确实对二者都进行了缓存,缓存的是变量对应的内存中存放的数据,于基本类型而言,缓存的是值,于对象类型而言,缓存的是对象所在内存区块的地址。我们在finally语句中进行操作时,不会影响缓存中存放的数据,这样无论我们对基本类型的值如何操作,返回值始终是之前的值,但是对象类型是不一样的,缓存的数据不变只能保证缓存数据地址指向的一直是同一个对象,finally语句块中对这个对象进行操作,是完全可以影响到对象的属性的。

    看到这,暂时算是豁然开朗了,也不知道有没有没挖出来的坑,如果有的话,还请各位大牛不吝赐教。

  • 相关阅读:
    JavaScript 检测对象类型
    memorize
    高阶函数
    JavaScript 中的不完全函数
    VS C# Winform 重写进度条
    使用ECLIPSE+RED搭建RF自动化测试框架
    NOIP2018濒死记
    HDU 5977 Garden of Eden (树形dp+快速沃尔什变换FWT)
    打FFT时中发现的卡常技巧
    Dynamic Rankings || 动态/静态区间第k小(主席树)
  • 原文地址:https://www.cnblogs.com/igoodful/p/9661680.html
Copyright © 2011-2022 走看看