有这样一段程序
public class Main {
public static void main(String[] args) {
new Main().call();
}
public int call() {
try {
int a = 1 / 0;
} catch (Exception e) {
System.err.println("exception");
return 0;
} finally {
return 4;
}
}
}
程序运行结果
java.lang.ArithmeticException: / by zero
4
我们来看下这段代码编译出来的部分字节码:
public int call();
descriptor: ()I
flags: ACC_PUBLIC
Code:
stack=2, locals=4, args_size=1
0: iconst_1
1: iconst_0
2: idiv
3: istore_1
4: iconst_4
5: ireturn
6: astore_1
7: getstatic #2 // Field java/lang/System.err:Ljava/io/PrintStream;
10: aload_1
11: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
14: iconst_0
15: istore_2
16: iconst_4
17: ireturn
18: astore_3
19: iconst_4
20: ireturn
Exception table:
from to target type
0 4 6 Class java/lang/Exception
0 4 18 any
6 16 18 any
可以看到,末尾的 Exceptions Table 就是异常表。异常表中的每一条记录,都代表了一个异常处理器。
- from 可能发生异常的起始点。
- to 可能发生异常的结束点。
- target 上述from和to之前发生异常后的异常处理者的位置。
- type 异常处理者处理的异常的类信息。
比如上面Exceptions Table ,在0-4位置,代表可能发生异常的位置,0-4位置的指令为:
0: iconst_1 // 将int型常量值1进栈
1: iconst_0 // int型常量值0进栈
2: idiv // 除
3: istore_1 //将栈顶int型数值存入第二个局部变量,从0开始计数
4: iconst_4
就是 int a =1/0; 代码。然后target 为6 ,表示异常后从6位置开始执行...
JVM指令大全
异常处理流程
- 当一个异常发生时,JVM会在当前出现异常的方法中,查找异常表,是否有合适的处理者来处理,如果当前方法异常表不为空,并且异常符合处理者的from和to节点,并且type也匹配,则JVM调用位于target的调用者来处理。
- 如果上一条未找到合理的处理者,则继续查找异常表中的剩余条目。
- 如果当前方法的异常表无法处理,则向上查找(弹栈处理)刚刚调用该方法的调用处,并重复上面的操作。
- 如果所有的栈帧被弹出,仍然没有处理,则抛给当前的Thread,Thread则会终止。
- 如果当前Thread为最后一个非守护线程,且未处理异常,则会导致JVM终止运行。
在看一个实例
public class Main {
public static void main(String[] args) {
System.out.println(getInt());
}
public static int getInt() {
int a = 10;
try {
System.out.println(a / 0);
a = 20;
} catch (ArithmeticException e) {
a = 30;
return a;
/*
* return a; 在程序执行到这一步的时候,这里不是return a; 而是return 30; 这个a变量是个副本。
* 但是呢,它发现后面还有finally,所以继续执行finally的内容,a=40 再次回到以前的返回路径,继续走 return a的副本30
*/
} finally {
a = 40;
// return a; // 如果这样结果就是40了。
}
return a;
}
}
结果运行
30
Java finally 语句到底是在 return 之前还是之后执行?
finally语句在return语句执行之后return返回之前执行的。
finally块中的return语句会覆盖try块中的return返回。
如果finally语句中没有return语句覆盖返回值,那么原来的返回值可能因为finally里的修改而改变也可能不变。
try块里的return语句在异常的情况下不会被执行,这样具体返回哪个看情况。
当发生异常后,catch中的return执行情况与未发生异常时try中return的执行情况完全一样。
finally 是否一定会执行
两种情况下 finally 不会执行
- try 模块没有运行。
- 使用
System.exit(0)
终止JVM。
throws关键字
此关键字主要在方法的声明上使用,表示方法中不处理异常,而交给调用处处理。
RuntimeExcepion与Exception的区别
Integer类: public static int parseInt(String text)throws NumberFormatException
此方法抛出了异常, 但是使用时却不需要进行try。。。catch捕获处理。
原因:
因为NumberFormatException并不是Exception的直接子类,而是RuntimeException的子类,只要是RuntimeException的子类,则表示程序在操作的时候可以不必使用try…catch进行处,如果有异常发生,则由JVM进行处理。当然,也可以通过try catch处理。