Exception 异常主要分为两类
受查异常就是指,编译器在编译期间要求必须得到处理的那些异常,你必须在编译期处理了
Java 的异常机制中所定义的所有异常不可能预见所有可能出现的错误,某些特定的情境下,则需要我们自定义异常类型来向上报告某些错误信息。
try/catch代码块中的代码称为保护代码,使用 try/catch 的语法如下:
try { // 程序代码 } catch(ExceptionName e1) { //Catch 块 }
try{ // 程序代码 }catch(异常类型1 异常的变量名1){ // 程序代码 }catch(异常类型2 异常的变量名2){ // 程序代码 }catch(异常类型2 异常的变量名2){ // 程序代码 }
public class className { public void deposit(double amount) throws RemoteException { // Method implementation throw new RemoteException(); } //Remainder of class definition }
一个方法可以声明抛出多个异常,多个异常之间用逗号隔开。
try{ // 程序代码 }catch(异常类型1 异常的变量名1){ // 程序代码 }catch(异常类型2 异常的变量名2){ // 程序代码 }finally{ // 程序代码 }
try-catch-finally 执行顺序的相关问题可以说是各种面试中的「常客」了,尤其是 finally 块中带有 return 语句的情况。我们直接看几道面试题:
public static void main(String[] args){ int result = test1(); System.out.println(result); } public static int test1(){ int i = 1; try{ i++; System.out.println("try block, i = "+i); }catch(Exception e){ i--; System.out.println("catch block i = "+i); }finally{ i = 10; System.out.println("finally block i = "+i); } return i; }
输出结果如下
try block, i = 2 finally block i = 10 10
这算一个相当简单的问题了,没有坑,下面我们稍微改动一下
public static int test2(){ int i = 1; try{ i++; throw new Exception(); }catch(Exception e){ i--; System.out.println("catch block i = "+i); }finally{ i = 10; System.out.println("finally block i = "+i); } return i; }
catch block i = 1 finally block i = 10 10
public static void main(String[] args){ int result = test3(); System.out.println(result); } public static int test3(){ //try 语句块中有 return 语句时的整体执行顺序 int i = 1; try{ i++; System.out.println("try block, i = "+i); return i; }catch(Exception e){ i ++; System.out.println("catch block i = "+i); return i; }finally{ i = 10; System.out.println("finally block i = "+i); } }
输出结果如下:
try block, i = 2 finally block i = 10 2
是不是有点疑惑?明明我 try 语句块中有 return 语句,可为什么最终还是执行了 finally 块中的代码?
我们反编译这个类,看看这个 test3 方法编译后的字节码的实现:
0: iconst_1 //将 1 加载进操作数栈 1: istore_0 //将操作数栈 0 位置的元素存进局部变量表 2: iinc 0, 1 //将局部变量表 0 位置的元素直接加一(i=2) 5: getstatic #3 // 5-27 行执行的 println 方法 8: new #5 11: dup 12: invokespecial #6 15: ldc #7 17: invokevirtual #8 20: iload_0 21: invokevirtual #9 24: invokevirtual #10 27: invokevirtual #11 30: iload_0 //将局部变量表 0 位置的元素加载进操作栈(2) 31: istore_1 //把操作栈顶的元素存入局部变量表位置 1 处 32: bipush 10 //加载一个常量到操作栈(10) 34: istore_0 //将 10 存入局部变量表 0 处 35: getstatic #3 //35-57 行执行 finally中的println方法 38: new #5 41: dup 42: invokespecial #6 45: ldc #12 47: invokevirtual #8 50: iload_0 51: invokevirtual #9 54: invokevirtual #10 57: invokevirtual #11 60: iload_1 //将局部变量表 1 位置的元素加载进操作栈(2) 61: ireturn //将操作栈顶元素返回(2) -------------------try + finally 结束 ------------ ------------------下面是 catch + finally,类似的 ------------ 62: astore_1 63: iinc 0, 1 ....... .......
可能有人会所疑惑,原本我们的 i 就被存储在局部变量表 0 位置,而最后 finally 中的代码也的确将 slot 0 位置填充了数值 10,可为什么最后程序依然返回的数值 2 呢?
public static int test4(){ //finally 语句块中有 return 语句 int i = 1; try{ i++; System.out.println("try block, i = "+i); return i; }catch(Exception e){ i++; System.out.println("catch block i = "+i); return i; }finally{ i++; System.out.println("finally block i = "+i); return i; } }
运行结果:
try block, i = 2 finally block i = 3 3
其实你从它的字节码指令去看整个过程,而不要单单记它的执行过程。
你会发现程序最终会采用 finally 代码块中的 return 语句进行返回,而直接忽略 try 语句块中的 return 指令。
最后,对于异常的使用有一个不成文的约定:尽量在某个集中的位置进行统一处理,不要到处的使用 try-catch,否则会使得代码结构混乱不堪。
原文:https://www.cnblogs.com/zouhong/p/13394327.html