很多人都认为 finally 语句块是肯定要执行的,其中也包括一些很有经验的 Java 程序员。可惜并不像大多人所认为的那样,对于这个问题,答案当然是否定的,我们先来看下面这个例子。
1 public class Test { 2 public static void main(String[] args) { 3 System.out.println("return value of test(): " + test()); 4 } 5 6 public static int test() { 7 int i = 1; 8 // if(i == 1) 9 // return 0; 10 System.out.println("the previous statement of try block"); 11 i = i / 0; 12 13 try { 14 System.out.println("try block"); 15 16 return i; 17 } finally { 18 System.out.println("finally block"); 19 } 20 } 21 22 } 23
清单 1 的执行结果如下:
the previous statement of try block Exception in thread "main" java.lang.ArithmeticException: / by zero at com.bj.charlie.Test.test(Test.java:15) at com.bj.charlie.Test.main(Test.java:6) |
另外,如果去掉上例中被注释的两条语句前的注释符,执行结果则是:
return value of test(): 0 |
在以上两种情况下,finally 语句块都没有执行,说明什么问题呢?
只有与 finally 相对应的 try 语句块得到执行的情况下,finally 语句块才会执行。
以上两种情况,都是在 try 语句块之前返回(return)或者抛出异常,所以 try 对应的 finally 语句块没有执行。
那好,即使与 finally 相对应的 try 语句块得到执行的情况下,finally 语句块一定会执行吗?
请看下面这个例子(清单 2)。
1 public class Test { 2 public static void main(String[] args) { 3 System.out.println("return value of test(): " + test()); 4 } 5 6 public static int test() { 7 int i = 1; 8 9 try { 10 System.out.println("try block"); 11 System.exit(0); 12 13 return i; 14 } finally { 15 System.out.println("finally block"); 16 } 17 } 18 }
清单 2 的执行结果如下:
try block |
finally 语句块还是没有执行,为什么呢?
因为我们在 try 语句块中执行了 System.exit (0) 语句,终止了 Java 虚拟机的运行。
那有人说了,在一般的 Java 应用中基本上是不会调用这个 System.exit(0) 方法的。
OK !没有问题,我们不调用 System.exit(0) 这个方法,那么 finally 语句块就一定会执行吗?
答案还是否定的。
当一个线程在执行 try 语句块或者 catch 语句块时被打断(interrupted)或者被终止(killed),与其相对应的 finally 语句块可能不会执行。
还有更极端的情况,就是在线程运行 try 语句块或者 catch 语句块时,突然死机或者断电,finally 语句块肯定不会执行了。
可能有人认为死机、断电这些理由有些强词夺理,没有关系,我们只是为了说明这个问题。
下面,我们先来看一个简单的例子(清单 3)。
1 public class Test { 2 public static void main(String[] args) { 3 try { 4 System.out.println("try block"); 5 6 return; 7 } finally { 8 System.out.println("finally block"); 9 } 10 } 11 }
清单 3 的执行结果为:
try block finally block |
清单 3 说明 finally 语句块在 try 语句块中的 return 语句之前执行
我们再来看另一个例子
1 public class Test { 2 public static void main(String[] args) { 3 System.out.println("reture value of test() : " + test()); 4 } 5 6 public static int test() { 7 int i = 1; 8 9 try { 10 System.out.println("try block"); 11 i = 1 / 0; 12 13 return 1; 14 } catch (Exception e) { 15 System.out.println("exception block"); 16 17 return 2; 18 } finally { 19 System.out.println("finally block"); 20 } 21 } 22 }
清单 4 的执行结果为:
try block exception block finally block reture value of test() : 2 |
清单 4 说明了 finally 语句块在 catch 语句块中的 return 语句之前执行。
从上面的清单 3 和清单 4,我们可以看出,
其实 finally 语句块是在 try 或者 catch 中的 return 语句之前执行的。
更加一般的说法是,finally 语句块应该是在控制转移语句之前执行,控制转移语句除了 return 外,还有 break 和 continue。
另外,throw 语句也属于控制转移语句。虽然 return、throw、break 和 continue 都是控制转移语句,但是它们之间是有区别的。
其中 return 和 throw 把程序控制权转交给它们的调用者(invoker),而 break 和 continue 的控制权是在当前方法内转移。
1 public class Test { 2 public static void main(String[] args) { 3 System.out.println("return value of getValue(): " + getValue()); 4 } 5 6 public static int getValue() { 7 try { 8 return 0; 9 } finally { 10 return 1; 11 } 12 } 13 }
清单 5 的执行结果:
return value of getValue(): 1 |
1 public class Test { 2 public static void main(String[] args) { 3 System.out.println("return value of getValue(): " + getValue()); 4 } 5 6 public static int getValue() { 7 int i = 1; 8 9 try { 10 return i; 11 } finally { 12 i++; 13 } 14 } 15 } 16
清单 6 的执行结果:
return value of getValue(): 1 |
利用我们上面分析得出的结论:
finally 语句块是在 try 或者 catch 中的 return 语句之前执行的。
由此,可以轻松的理解清单 5 的执行结果是 1。
因为 finally 中的 return 1;语句要在 try 中的 return 0;语句之前执行,那么 finally 中的 return 1;
语句执行后,把程序的控制权转交给了它的调用者 main()函数,并且返回值为 1。
那为什么清单 6 的返回值不是 2,而是 1 呢?
按照清单 5 的分析逻辑,finally 中的 i++;
语句应该在 try 中的 return i;之前执行啊?
i 的初始值为 1,那么执行 i++;之后为 2,再执行 return i;那不就应该是 2 吗?怎么变成 1 了呢?
我们来分析一下其执行顺序:分为正常执行(没有 exception)和异常执行(有 exception)两种情况。我们先来看一下正常执行的情况,如图 1 所示:
由上图,我们可以清晰的看出,在 finally 语句块(iinc 0, 1)执行之前,getValue()方法保存了其返回值(1)到本地表量表中 1 的位置,完成这个任务的指令是 istore_1;然后执行 finally 语句块(iinc 0, 1),finally 语句块把位于 0 这个位置的本地变量表中的值加 1,变成 2;待 finally 语句块执行完毕之后,把本地表量表中 1 的位置上值恢复到操作数栈(iload_1),最后执行 ireturn 指令把当前操作数栈中的值(1)返回给其调用者(main)。这就是为什么清单 6 的执行结果是 1,而不是 2 的原因。
再让我们来看看异常执行的情况。是不是有人会问,你的清单 6 中都没有 catch 语句,哪来的异常处理呢?我觉得这是一个好问题,其实,即使没有 catch 语句,Java 编译器编译出的字节码中还是有默认的异常处理的,别忘了,除了需要捕获的异常,还可能有不需捕获的异常(如:RunTimeException 和 Error)。
1 public class Test { 2 public static void main(String[] args) { 3 System.out.println("return value of getValue(): " + getValue()); 4 } 5 6 @SuppressWarnings("finally") 7 public static int getValue() { 8 int i = 1; 9 10 try { 11 i = 4; 12 } finally { 13 i++; 14 15 return i; 16 } 17 } 18 }
清单 7 的执行结果:
return value of getValue(): 5 |
1 public class Test { 2 public static void main(String[] args) { 3 System.out.println("return value of getValue(): " + getValue()); 4 } 5 6 public static int getValue() { 7 int i = 1; 8 9 try { 10 i = 4; 11 } finally { 12 i++; 13 } 14 15 return i; 16 } 17 }
清单 8 的执行结果:
return value of getValue(): 5 |
让我们再来看一个稍微复杂一点的例子 – 清单 9。
1 public class Test { 2 public static void main(String[] args) { 3 System.out.println(test()); 4 } 5 6 public static String test() { 7 try { 8 System.out.println("try block"); 9 10 return test1(); 11 } finally { 12 System.out.println("finally block"); 13 } 14 } 15 16 public static String test1() { 17 System.out.println("return statement"); 18 19 return "after return"; 20 } 21 }
清单 9 的结果:
try block return statement finally block after return |
return test1();这条语句等同于 :
String tmp = test1(); return tmp; |
这样,就应该清楚为什么是上面所示的执行结果了吧!
相关推荐
在Java编程语言中,`finally`语句块是一个非常关键的结构,用于确保特定代码无论在任何情况下(包括异常发生或正常返回)都会被执行。这个特性使得`finally`成为处理资源清理,如关闭文件流、数据库连接等操作的理想...
`try-catch-finally`结构是Java提供的一种优雅的处理异常的方式,确保了程序在遇到错误时能正常运行并有机会清理资源。下面我们将详细讨论这个结构的工作原理和使用方法。 ### 1. `try` 块 `try`块是用来包含可能...
Java 中 finally 语句块与 return 的执行关系 Java 中的 finally 语句块是用于保证无论出现什么情况,一定要执行的代码块。在 try-catch-finally 结构中,finally 语句块的执行顺序是非常重要的。下面我们来详细...
Java finally语句到底是在return之前还是之后执行?Java finally执行深度剖析,具体看这篇博文:http://blog.csdn.net/lanxuezaipiao/article/details/16922895,这是里面相关的源码,欢迎大家下载使用。
try-catch-finally语句是Java语言中一种常用的异常处理机制,当程序在执行过程中出现异常时,可以使用try-catch-finally语句来捕获和处理异常。下面将详细介绍try-catch-finally语句的使用方法和注意事项。 一、try...
Java中的`finally`块是用来确保某些代码无论是否抛出异常都会被执行的关键结构。它常常与`try-catch`块一起使用,特别是在处理资源清理,如关闭文件流或网络连接等操作时。`finally`块的执行时机是多方面的,下面...
Java中的`finally`关键字是异常处理机制的重要组成部分,它的主要作用是确保在程序执行过程中,不论是否发生异常,某些特定的代码段总会被执行。这在处理资源清理、保证程序完整性和防止资源泄露等方面非常关键。 ...
MySQL是世界上最受欢迎的关系型数据库管理系统之一,而`mysql-connector-java-5.1.30-bin.jar`是一个针对Java应用程序的MySQL驱动程序,允许Java开发者通过编程方式与MySQL数据库进行交互。这个压缩包包含了必要的...
Java 中 finally 语句的执行顺序探讨 在 Java 中,finally 语句的执行顺序是一个常见的探讨话题。很多人都认为 finally 语句一定会被执行,但这是不正确的。在某些情况下,finally 语句可能不会被执行,例如在 try ...
在Java编程中,`finally`语句块是一个关键的组成部分,尤其在异常处理中起到至关重要的作用。然而,关于`finally`的执行原理,并非所有程序员都对其有深入的理解。`finally`的主要功能是在程序执行过程中确保某些...
Java提供了try、catch和finally三种语句来捕获和处理异常。 一、try语句 try语句是一个块语句,用于包围可能出现异常的代码。try语句的格式为: ```java try { // 可能出现异常的代码 } ``` try语句中的代码可能...
Java语言finally语句详解,finally到底是在return之前还是之后执行.zip
在Java编程语言中,TCP(Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。本文将深入探讨如何在Java中实现一个TCP客户端,并结合提供的文件"CLIENT_TCP.java"来解析其核心...
在Java编程中,为了实现与SQL Server 2008数据库的交互,我们需要引入特定的JDBC驱动程序,也就是Java Database Connectivity(Java数据库连接)驱动。这个驱动允许Java应用程序通过API来执行SQL语句,从而读取、...
Java Socket 通讯例程是Java网络编程中的基础概念,它提供了两台计算机间低级别的通信通道。在这个例程中,我们通常会创建一个服务器端(Server)应用和一个客户端(Client)应用,通过Socket实现数据的交换。下面,...
Java Telnet客户端是一种基于TCP协议的应用程序,它允许用户通过Java编程语言与远程服务器上的Telnet服务进行交互。在Java中实现一个Telnet客户端可以帮助开发者远程控制或测试其他支持Telnet的服务,例如路由器配置...
17.javatry…catch…finally语句.zip17.javatry…catch…finally语句.zip17.javatry…catch…finally语句.zip17.javatry…catch…finally语句.zip17.javatry…catch…finally语句.zip17.javatry…catch…finally语句...
在Java编程中,读取文本文件是常见的任务之一,这对于处理数据、日志记录或从文件中获取配置信息等场景非常有用。以下是如何使用Java API来读取文本文件的详细步骤,以标题“Java如何读取文本文件”和提供的代码为例...
本文将深入探讨Java中的`try-catch-finally`语句块,以及它们在异常处理中的作用。 首先,`try`块是异常处理的起点。在这个代码段中,我们通常会放置可能会抛出异常的代码。当Java执行到可能抛出异常的代码行时,...
Java中的异常处理主要涉及以下几个关键字:`try`、`catch`、`finally`、`throws`和`throw`。 ##### 2.1 `try`和`catch` `try`块用来包裹可能抛出异常的代码,而`catch`块则用来捕获并处理这些异常。可以有多个`...