- 浏览: 931969 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (251)
- WebService (17)
- IBatis (22)
- Hibernate (1)
- SpringMVC - 基础篇 (32)
- Spring (15)
- Java (11)
- JVM及调优 - 基础篇 (4)
- 集群 (14)
- 数据库 (17)
- WebSphere (5)
- 多线程 (4)
- 集合、容器 (2)
- DB Pool (1)
- Power Designer (5)
- Maven基础 (5)
- JS (14)
- WEB 前端 (5)
- 实用小工具 (17)
- 社会、人 (2)
- 乱七八糟 (18)
- ASM&CGLIB - 基础篇 (12)
- 缓存 (1)
- 性能 (1)
- 设计之殇 (1)
- 分布式事务 (1)
- 单点登录 (11)
- 分布式 Session (4)
- Memcached - 基础篇 (6)
最新评论
-
一笑_奈何:
楼主写的还真行不错。
扫盲贴 - J2EE集群之JNDI集群实现 -
xuezhongyu01:
博主写的很详细,但最后还是没明白,最后调用BasicDataS ...
Spring中的destroy-method方法 -
Mr梁:
commons-fileupload.jar commons- ...
SpringMVC 中文件上传 MultipartResolver -
Eywa:
总结的很不错
ORACLE CASE WHEN 及 SELECT CASE WHEN的用法 -
TryRelax:
fastjson 比 jackson 好用吧?
Spring MVC Jackson DateFormat
Java 中的finally你知多少
http://henryyang.iteye.com/blog/1240926
public class Test {
public static void main(String[] args) {
System.out
.println("return value of getValue(): " + getValue
());
}
public static int getValue() {
try {
return 0;
} finally {
return 1;
}
}
}
|
清单 5 的执行结果:
return value of getValue(): 1
|
public class Test {
public static void main(String[] args) {
System.out
.println("return value of getValue(): " + getValue
());
}
public static int getValue() {
int i = 1;
try {
return i;
} finally {
i++;
}
}
}
|
清单 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 了呢?
关于 Java 虚拟机是如何编译 finally 语句块的问题,有兴趣的读者可以参考《 The JavaTM Virtual Machine Specification, Second Edition 》中 7.13 节 Compiling finally。那里详细介绍了 Java 虚拟机是如何编译 finally 语句块。实际上,Java 虚拟机会把 finally 语句块作为 subroutine(对于这个 subroutine 不知该如何翻译为好,干脆就不翻译了,免得产生歧义和误解。)直接插入到 try 语句块或者 catch 语句块的控制转移语句之前。但是,还有另外一个不可忽视的因素,那就是在执行 subroutine(也就是 finally 语句块)之前,try 或者 catch 语句块会保留其返回值到本地变量表(Local Variable Table)中。待 subroutine 执行完毕之后,再恢复保留的返回值到操作数栈中,然后通过 return 或者 throw 语句将其返回给该方法的调用者(invoker)。请注意,前文中我们曾经提到过 return、throw 和 break、continue 的区别,对于这条规则(保留返回值),只适用于 return 和 throw 语句,不适用于 break 和 continue 语句,因为它们根本就没有返回值。
是不是不太好理解,那我们就用具体的例子来做形象的说明吧!
为了能够解释清单 6 的执行结果,我们来分析一下清单 6 的字节码(byte-code):
Compiled from "Test.java"
public class Test extends java.lang.Object{
public Test();
Code:
0: aload_0
1:invokespecial#1; //Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0
public static void main(java.lang.String[]);
Code:
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: new #3; //class java/lang/StringBuilder
6: dup
7: invokespecial #4; //Method java/lang/StringBuilder."<init>":()V
10: ldc #5; //String return value of getValue():
12: invokevirtual
#6; //Method java/lang/StringBuilder.append:(
Ljava/lang/String;)Ljava/lang/StringBuilder;
15: invokestatic #7; //Method getValue:()I
18: invokevirtual
#8; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
21: invokevirtual
#9; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
24: invokevirtual #10; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
27: return
public static int getValue();
Code:
0: iconst_1
1: istore_0
2: iload_0
3: istore_1
4: iinc 0, 1
7: iload_1
8: ireturn
9: astore_2
10: iinc 0, 1
13: aload_2
14: athrow
Exception table:
from to target type
2 4 9 any
9 10 9 any
}
|
对于 Test()构造方法与 main()方法,在这里,我们不做过多解释。让我们来分析一下 getValue()方法的执行。在这之前,先让我把 getValue()中用到的虚拟机指令解释一下,以便读者能够正确的理解该函数的执行。
1. iconst_
Description: Push the int constant (-1, 0, 1, 2, 3, 4 or 5) onto the operand stack.
Forms: iconst_m1 = 2 (0x2) iconst_0 = 3 (0x3) iconst_1 = 4 (0x4)
iconst_2 = 5 (0x5) iconst_3 = 6 (0x6) iconst_4 = 7 (0x7) iconst_5 = 8 (0x8)
2. istore_
Description: Store int into local variable. The must be an index into the
local variable array of the current frame.
Forms: istore_0 = 59 (0x3b) istore_1 = 60 (0x3c) istore_2 = 61 (0x3d)
istore_3 = 62 (0x3e)
3. iload_
Description: Load int from local variable. The must be an index into the
local variable array of the current frame.
Forms: iload_0 = 26 (0x1a) iload_1 = 27 (0x1b) iload_2 = 28 (0x1c) iload_3 = 29 (0x1d)
4. iinc index, const
Description: Increment local variable by constant. The index is an unsigned byte that
must be an index into the local variable array of the current frame. The const is an
immediate signed byte. The local variable at index must contain an int. The value
const is first sign-extended to an int, and then the local variable at index is
incremented by that amount.
Forms: iinc = 132 (0x84)
Format:
iinc
index
const
5. ireturn
Description: Return int from method.
Forms: ireturn = 172 (0xac)
6. astore_
Description: Store reference into local variable. The must be an index into the
local variable array of the current frame.
Forms: astore_0 = 75 (0x4b) astore_1 = 76 (0x4c) astore_2 =77 (0x4d) astore_3 =78 (0x4e)
7. aload_
Description: Load reference from local variable. The must be an index into the
local variable array of the current frame.
Forms: aload_0 = 42 (0x2a) aload_1 = 43 (0x2b) aload_2 = 44 (0x2c) aload_3 = 45 (0x2d)
8. athrow
Description: Throw exception or error.
Forms: athrow = 191 (0xbf)
|
有了以上的 Java 虚拟机指令,我们来分析一下其执行顺序:分为正常执行(没有 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)。
从 getValue()方法的字节码中,我们可以看到它的异常处理表(exception table), 如下:
Exception table:
from to target type
2 4 9 any
它的意思是说:如果从 2 到 4 这段指令出现异常,则由从 9 开始的指令来处理。
先说明一点,上图中的 exception 其实应该是 exception 对象的引用,为了方便说明,我直接把它写成 exception 了。
由上图(图 2)可知,当从 2 到 4 这段指令出现异常时,将会产生一个 exception 对象,并且把它压入当前操作数栈的栈顶。接下来是 astore_2 这条指令,它负责把 exception 对象保存到本地变量表中 2 的位置,然后执行 finally 语句块,待 finally 语句块执行完毕后,再由 aload_2 这条指令把预先存储的 exception 对象恢复到操作数栈中,最后由 athrow 指令将其返回给该方法的调用者(main)。
通过以上的分析,大家应该已经清楚 try-catch-finally 语句块的执行流程了吧!
为了更具说服力,我们还是来引经据典吧!大家可以不相信我,难道还不相信“高司令”(Gosling)吗?下面这段仍然摘自 Java 语言规范第四版 《 The Java™ Programming Language, Fourth Edition 》 ,请读者自己体会吧!
*******************************************************************************
a finally clause is always entered with a reason. That reason may be that the try code finished normally, that it executed a control flow statement such as return, or that an exception was thrown in code executed in the Try block. The reason is remembered when the finally clause exits by falling out the bottom. However, if the finally block creates its own reason to leave by executing a control flow statement (such as break or return) or by throwing an exception, that reason supersedes the original one, and the original reason is forgotten. For example, consider the following code:
try {
// … do something …
return 1;
} finally {
return 2;
}
When the Try block executes its return, the finally block is entered with the “reason” of returning the value 1. However, inside the finally block the value 2 is returned, so the initial intention is forgotten. In fact, if any of the other code in the try block had thrown an exception, the result would still be to return 2. If the finally block did not return a value but simply fell out the bottom, the “return the value 1 ″ reason would be remembered and carried out.
*******************************************************************************
好了,有了以上的知识,让我们再来看以下 3 个例子。
发表评论
-
Fastjson内幕
2013-08-22 15:24 0转载:http://code.alibabatech.com ... -
【转】JAVA中重写equals()方法为什么要重写hashcode()方法说明【三】
2013-07-21 15:25 717重写equals()方法 下 ... -
【转】JAVA中重写equals()方法为什么要重写hashcode()方法说明【二】
2013-07-21 15:21 13471. 关于Object类理 ... -
【转】JAVA中重写equals()方法为什么要重写hashcode()方法说明【一】
2013-07-21 15:11 1679重写hashCode()时最重要的原因就是:无论何时,对同 ... -
Dozer 使用
2013-07-14 10:33 12501.1 什么是dozer?Dozer 是一个对象转换工具。D ... -
java序列化和serialVersionUID
2013-04-22 16:39 530转载:http://luan.iteye.com/blog/ ... -
JSP自定义标签2.0
2012-06-09 13:04 1901转载 http://www.ibm.com/deve ... -
JSP自定义标签1.1
2012-06-09 11:17 1113首先我们需要大致了解 ... -
*.jspf扩展名文件
2011-11-30 09:51 5133网上查找的资料,对这个jspf扩展名文件还是不很理解;如果哪位 ... -
Java 序列化的高级认识
2011-11-19 17:18 825Java 序列化的高级认识 以前只是简单的使用,还没有 ... -
java.text.ParseException: Unparseable date: 2011-11-8
2011-11-08 14:37 3273怎样将 2011-11-8 转成 2011年11月8日 ? ...
相关推荐
这份包含36篇文章的文档集合,旨在深入剖析Java中的不为人知的问题,提供程序员必须掌握的关键知识点,以及如何应对面试中可能遇到的问题。下面将详细讨论这些文档可能涵盖的内容。 1. **内存管理与垃圾回收**:...
Java是世界上最流行的编程语言之一,尤其在企业级应用开发领域占据主导地位。这份"JAVA核心知识点整理——java...这份资料“JAVA自学心路资料整理.pdf”无疑是这样的一个宝贵资源,它将带你逐步探索Java的广阔世界。
在准备Java后端面试的过程中,面试者需要掌握一系列的知识点,这些知识点涵盖Java基础知识、高级特性、框架应用、性能优化、设计模式以及系统架构等方面。下面将详细解释给定文件中提到的一些关键知识点: 1. Java...
17. 资源 close(): JAVA 编程规范建议数据库操作、IO 操作等需要使用结束 close() 的对象必须在 try-catch-finally 的finally 中 close()。 18. 数据类的 toString() 方法: JAVA 编程规范建议所有的数据类必须...
5. **异常处理**:Java异常处理是通过`try-catch-finally`块进行的,用于处理运行时错误。实例中会展示如何有效地捕获和处理异常。 6. **集合框架**:Java集合框架包括List、Set、Map等接口及其实现类,如ArrayList...
3.4 小结:基本数据类型—— Java中一切数据和运算的基础 63 3.5 习题 65 第4章 Java中的程序执行流程 67 教学视频:1小时57分钟 4.1 顺序执行 67 4.2 使用if-else让程序懂得判断 68 4.2.1 if语句 68 4.2.2 ...
2. **异常处理**:Java中的异常处理机制是面试的常见话题,包括try-catch-finally块、检查异常和运行时异常的区别,以及如何自定义异常。 3. **内存管理与垃圾回收**:理解Java的内存模型,包括堆内存、栈内存和...
以上只是Java核心技术的一部分,实际书中还会涵盖更多高级主题,如网络编程、数据库连接、GUI编程、Swing或JavaFX应用开发、并发控制、性能优化等。通过阅读和实践,开发者可以掌握Java编程的全方位技能,为构建复杂...
"Java面试必知必会Gothic主题"可能包含了一系列与Java核心技术、面试技巧以及常见问题相关的资料。下面我们将深入探讨一些Java面试中常见的核心知识点。 1. **基础语法**:面试通常会从Java的基础开始,如数据类型...
3.4 小结:基本数据类型—— Java中一切数据和运算的基础 63 3.5 习题 65 第4章 Java中的程序执行流程 67 教学视频:1小时57分钟 4.1 顺序执行 67 4.2 使用if-else让程序懂得判断 68 4.2.1 if语句 68 4.2.2 ...
4. **多线程**: 多线程是Java后端开发中常见的话题。这包括线程的创建(Thread类和Runnable接口),线程同步(synchronized关键字,wait()和notify(),Lock接口等),以及并发集合类的使用。 5. **集合框架**: 熟悉...
5. Java中的异常处理:讨论了try、catch、finally以及throws等关键字的使用,如何在代码中处理运行时错误。 6. Java内部类和匿名内部类:解释内部类如何访问外部类成员,以及匿名内部类的使用和特点。 7. Java中的类...
实训代码会通过实际的程序示例,让你掌握如何在Java中声明、初始化和使用变量,理解各种数据类型,以及如何运用条件判断和循环结构来控制程序执行流程。 2. **面向对象编程**: Java 是一种面向对象的语言,它支持类...
它不仅涵盖了Java编程语言的核心概念和技术,更深入到程序员的生活状态、职业发展和心理历程,试图揭示这个行业中不为人知的一面。以下是基于这个主题的Java知识点详细说明: 1. **Java编程基础**:作为一门广泛...
- 在处理用户输入或系统运行时可能出现的问题时,Java的异常处理(try-catch-finally)机制能确保程序的健壮性。例如,如果用户输入非法,系统可以通过捕获并处理异常来防止程序崩溃。 4. **集合框架**: - 系统...
第一,谈谈final, finally, finalize的区别。 final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。finally是异常处理语句结构的一部分,表示总是执行。finalize是 Object类的一个方法...
一、Java必知必会108题 这部分内容可能涵盖了Java基础语法、面向对象编程、集合框架、多线程、异常处理、I/O流、网络编程、反射机制、JVM内存模型等多个方面。例如: 1. Java的基础语法:变量声明、数据类型、运算符...
### JAVA入门日文版知识点详解 #### 1. クラス(Class) - **クラスの定義**: クラスはオブジェクトの設計図であり、...これらの基本的な知識を理解することは、JAVAプログラミングの基礎を固める上で非常に重要です。
* 约定俗成:Java标识符选取因注意“见名知意”且不能与Java语言的关键字重名。 关键字 Java中一些赋以特定的含义,用做专门用途的字符串称为关键字(keyword)。所有Java关键字都是小写英文字符串。goto和const...