try-catch-finally结合体是处理程序中异常抛出的代码块,try-catch-finally有很多属于他们群体的游戏规则,生硬地记忆会容易混淆,这里这样打比方:如果try是皇帝,catch是妃子,finally是皇后,那么:
1)妃子(catch)可以一个或者多个。皇后(finally)只有一个。
//try-catch-finally中,可能会抛出多个异常,可以用多个catch去捕捉,但finally始终只有一个。
2)try(皇帝)有了妃子(catch),就可以不管皇后(finally)。
//try后边可以只有catch,没有finally.
3)但try没有妃子(catch)的日子,可以单独和皇后(finally)在一起。
//try当然也可以没有catch,只跟着finally.
4)皇帝(try)不能单独一个人生活。
//try单独出现、后边没有catch和finally是错误的。
5)皇帝(try)一抛异常,那就退朝了,异常后边的事情都不管了。
//如果try代码块中,一抛出异常,异常后边的代码都不会执行。
6)如果皇帝(try)有什么异常,只有妃子(catch)们兜着,但最后收拾烂滩子的只有皇后(finally)。
//如果try抛出异常,catch去捕捉,但只要finally存在,一定会执行finally的代码。
7)如果妃子(catch)们顺利捕捉即使不处理,宫内秩序一切恢复正常,该溜鸟的溜鸟,该赏花的赏花。
//如果catch捕捉到了异常,即使处理代码为空,程序还将执行try-catch-finally块下方的代码。
8)如果皇帝(try)抛出的异常不是妃子们没有能力捕捉到,不执行后边的动作,向他们父母禀报,如果父母不处理,大家都死翘翘。
//如果抛出的异常没有catch到,那向上抛出,交给调用者处理。如果始终程序没有谁对此异常进行响应,那么程序中止,由main方法最终向用户抛出。这是程序员要极力避免的。
9) 宫里出现了刺客,该抓的抓,该杀的杀。但如果一切结束之后,内奸还隐藏在宫中的话,也需要捕捉到底。
//如果try里抛出的异常被catch到,并处理了。而执行了finally块,但如果finally块里边居然还有异常抛出,必须坚定地在里边再进行try-catch。要不然程序依然会因为异常而暂停执行下去。
下面我们详细讨论下try-catch-finally的使用过程中的疑问,先看这个程序:
public class TestTest {
static String temp = null;
public static void main(String[] args) {
try{
return;
}catch(Exception e){
temp = "catch";
}finally{
temp = "finally";
}
System.out.println(temp);
}
}
在这个程序中,如果执行没有输出,直觉上,temp没有赋值。使我们有理由相信try-catch-finally代码块,在try已经return的情况下,finally将不会被执行。事实上是finally是执行了,没有输出是因为return是main方法的return,直接返回的话,是已经退出了JVM,所以System.out没有被执行到。只要有finally,语法上正确的语句,天打雷劈也会去执行finally的代码体的,无论try处还是catch处出现了多少个return语句。不过仔细一想,话好象说过了。如果在try代码块和catch代码块里边,执行到exit()这样的语句或者抛出了error这种致命错误,finally也是不会执行。
如果catch块中包括多个异常类,这些异常类的匹配原则是什么?接catch块中异常类的排列顺序,寻找第一个能够匹配(也就是做出的异常对象是catch块的异常类,或是属于catch块中异常类的子类),如果你图省事在第一顺位就安排最牛的Exception守着,后边的小异常放在后边,编译器不干了,提示:Unreachable catch block for ArithmeticException. It is already handled by the catch block for Exception。
程序抛出了异常,但在方法体内就被成功捕获,程序可以恢复正常执行。如果但在脱离try-catch-finally代码块时,还是必须执行finally语句;如果程序抛出,本地没有解决掉,向上抛出throws Exception,那么如果这个异常上层没有解决,程序也无法执行下去,如果调用返回已经被上层捕获并处理,程序继续。同样,即使上层没有处理掉此异常,finally还是会执行它的代码体。如下程序所示:
public class P204 {
public static void main(String[] args) {
int k=0;
try{
int i=5/k;
}catch(ArithmeticException e){ //这里被捕捉之后,后边的catch只是摆着看看的。
System.out.print("1");
}catch(RuntimeException e){
System.out.print("2");
return;
}catch(Exception e){
System.out.print("3");
}finally{
System.out.print("4"); //无论出现什么情况,此句一定会执行
}
System.out.print("5");
}
}
程序输出:145
在方法覆盖时,子类方法抛出的异常类对象,要么是父类抛出异常的子集,要么是你类抛出的子类对象,反正,不能越权越界抛出。
class AA{
public void f() throws Exception{}
}
class BB extends AA{
public void f() throws InterruptedException{}
}
或者:
class AA{
public void f() throws InterruptedException,ArithmeticException{}
}
class BB extends AA{
public void f() throws InterruptedException{}
}
声明自己抛出特定异常类的方法 可以抛出该异常类的任何子类实例。比如我声明我要抛出一堆钱,如果我抛出硬币,1块,10块,都可以。这句话的理解,还是看下边的程序:
public static void main(String[] args) throws ArithmeticException {
try {
f();
} catch (Exception e) {
throw new ArithmeticException(); //ArithmeticException是方法抛出异常类子类
}
}
public static void f()throws Exception{ //这个方法抛出一个异常类对象
throw new Exception();
}
}
最后,还简单提一下Error,它是系统错误,不需要被捕捉,但它可以被捕捉,如下例:
public class Gotcha {
public static void main(String[] args) {
try{
new Gotcha().go();
}
catch(Error e){ //这里装腔作势捕捉一下的目的,实际价值是在于真的出错了的情况下,做些小弥补。输出一点信息。
System.out.println(e);
}
}
void go(){ //一个不负责任的递归,肯定会导致StackOverflowError。
go();
}
}