1.異常機(jī)制
Unchecked Exception
上面的異常繼承體系中多柑,Error和RuntimeException是非檢查型異常(Unchecked Exception),也就是不需要catch語句去捕獲的異常安皱;其他異常,則需要程序員手動(dòng)去處理。
接下來讓我們通過字節(jié)碼去一探究竟這種異常處理。
2.異常表
我們先來看一個(gè)實(shí)例
package sandwich.test5;
/**
* @author 公眾號(hào):IT三明治
* @date 2021/6/13
*/
public class SynchronizedTest {
synchronized void fun1(){
System.out.println("fun1");
}
static synchronized void fun2(){
System.out.println("fun2");
}
final Object lock=new Object();
void lockProcess(){
synchronized (lock){
System.out.println("lock");
}
}
}
反編譯得到lockProcess的信息
lockProcess方法反編譯
在synchronized生成的字節(jié)碼中临谱,其實(shí)包含兩條monitorexit指令,是為了保證所有的異常條件奴璃,都能夠退出悉默。
編譯后的字節(jié)碼,有一個(gè)叫Exception table的異常表苟穆,里面的每一行數(shù)據(jù)抄课,都是一個(gè)異常處理器。
from: 指定字節(jié)碼索引的開始位置
to: 指定字節(jié)碼索引的結(jié)束位置
target: 異常處理的起始位置
type:異常類型
也就是說雳旅,只要在from和to之間發(fā)生了異常跟磨,就會(huì)跳到target所指定的位置。
第一條monitorexit(16)在異常表第一條的范圍中攒盈,如果異常抵拘, 能夠跳到第20行。
第二條monitorexit(22)在異常表第二條的范圍中沦童,如果異常仑濒,能夠跳轉(zhuǎn)到第20行。
由以上java代碼可以看到偷遗,我們不需要主動(dòng)去catch exception. 也能自動(dòng)處理兩個(gè)any類型的異常
如果用jclasslib插件觀察墩瞳,也很方便。
3. Finally
通常我們?cè)谧鲆恍┪募x取的時(shí)候氏豌,都會(huì)在finally代碼塊中關(guān)閉流喉酌,以避免內(nèi)存的溢出。關(guān)于這個(gè)場(chǎng)景泵喘,我們?cè)俜治鲆幌孪旅孢@段代碼的異常表泪电。
package sandwich.test5;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
/**
* @author 公眾號(hào):IT三明治
* @date 2021/6/13
*/
public class StreamTest {
public void read() {
InputStream in = null;
try {
in = new FileInputStream("Test.java");
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (null != in) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
上面的代碼,捕獲了一個(gè)FileNotFoundException異常纪铺,然后在finally中捕獲了IOException異常相速。當(dāng)我們分析字節(jié)碼的時(shí)候,卻發(fā)現(xiàn)了一個(gè)有意思的地 方:IOException足足出現(xiàn)了三次
finally異常處理過程
Java編譯器使用了一種比較傻的方式來組織finally的字節(jié)碼鲜锚,它分別在try突诬、catch的正常執(zhí)行路徑上,復(fù)制一份finally代碼芜繁,追加在正常執(zhí)行邏輯的后面旺隙。同時(shí),再?gòu)?fù)制一份到其他異常執(zhí)行邏輯的出口處骏令。
再看一個(gè)例子
package sandwich.test5;
/**
* @author 公眾號(hào):IT三明治
* @date 2021/6/13
*/
public class NoError {
public static void main(String[] args) {
NoError noError = new NoError();
int result = noError.read();
System.out.println(result);
}
private int read() {
try {
int a = 13/0;
return a;
} finally {
return 1;
}
}
}
這段代碼不報(bào)錯(cuò)的原因蔬捷,都可以在字節(jié)碼中找到答案
通過程序的字節(jié)碼,可以看到榔袋,異常之后周拐,直接跳轉(zhuǎn)到序號(hào)9了。沒有輸出錯(cuò)誤的字節(jié)碼摘昌。