異常簡介
異常指阻止當前方法繼續執行的問題,如:文件找不到、網絡連接失敗、非法參數等。發現異常的理想時期是編譯階段,然而編譯期間.不能找出所有的異常,余下的問題須在運行期間.
java中的異常分為可查異常和不可查異常
- 可查異常
即編譯時異常,指編譯器在編譯時可以發現的錯誤,程序在運行時很容易出現的異常狀況,這些異??梢灶A計,所以在編譯階段就必須手動進行捕捉處理,即要么用try-catch語句捕獲它,要么用throws子句聲明拋出,否則編譯無法通過。如IOException、SQLException以及用戶自定義的Exception異常 - 不可查異常
不可查異常包括運行時異常(runtimeException)和錯誤(error),他們都是在程序運行時出現的。異常和錯誤的區別:異常能被程序本身可以處理,錯誤是無法處理
運行時異常(runtimeException)指的是程序在運行時才會出現的錯誤,由程序員自己分析代碼決定是否用try...catch進行捕捉處理。如nullpointerException,classcastException,indexoutofboundsException。
錯誤(error),是程序無法處理的錯誤,表示運行應用程序中較嚴重問題,如系統崩潰,虛擬機錯誤,動態連接失敗等。大多數錯誤與代碼編寫者執行的操作無關,而表示代碼運行時JVM(Java虛擬機)出現的問題。例如,Java虛擬機運行錯誤(VirtualMachineError),當JVM不再有繼續執行操作所需的內存資源時,將出現OutOfMemoryError。這些異常發生時,Java虛擬機(JVM)一般會選擇線程終止。這些錯誤表示故障發生于虛擬機自身、或者發生在虛擬機試圖執行應用時,如Java虛擬機運行錯誤(VirtualMachineError)、類定義錯(NoClassDefFoundError)等。這些錯誤是不可查的,即不需要捕獲和處理,因為它們在應用程序的控制和處理能力之外,而且絕大多數是程序運行時不允許出現的狀況。對于設計合理的應用程序來說,即使確實發生了錯誤,本質上也不應該試圖去處理它所引起的異常狀況。
Java異常類層次結構圖:
從上圖可以看出Java通過API中Throwable類的眾多子類描述各種不同的異常。因而,Java異常都是對象,是Throwable子類的實例.
在Java中,所有的異常都有一個共同的祖先Throwable(可拋出)。Throwable指定代碼中可用異常傳播機制通過Java應用程序傳輸的任何問題的共性。
異常處理機制
當異常發生時,將使用new在堆上創建一個異常對象,對于這個異常對象,有兩種處理方式.
1.使用throw關鍵字將異常對象拋出,則當前執行路徑被終止,異常處理機制將在其他地方尋找catch塊對異常進行處理.
2.使用try...catch在當前邏輯中就進行捕獲處理.
throws 和throw
- throws: 一個方法在聲明時可以使用throws關鍵字聲明可能會產生的若干異常。
- throw: 拋出異常,并退出當前方法或作用域。
用throws聲明要拋出的異常,實際可以不拋出。而用throw拋出的異常也可以不聲明。不過Java鼓勵程序員把可能會拋出的異常提前聲明,這是一種優雅的做法。
自定義異常
Java的異常體系不可能包括所有的異常情況,所以可以自己定義異常類來表示程序中可能會遇到的特定問題。
public class MyException extends RuntimeException {
public MyException() {
}
public MyException(String message) {
super(message);
}
}
在 MyException 這個自定義異常中,定義了兩個構造器。一個是默認構造器,一個接收一個字符串作為參數。在第二個構造器中,可以看出調用了基類構造器,所傳字符串可以通過getMessage()方法獲取。
public class ExceptionTest {
public static void f(){
System.out.println("Throwing Exception from f()");
throw new MyException();
}
public static void g(){
System.out.println("Throwing Exception from g()");
throw new MyException("Originated in g()");
}
public static void main(String[] argv) throws Exception {
try{
f();
}catch(MyException e){
System.out.println(e.getMessage());
e.printStackTrace(System.out);
}
}
}
在main函數中調用g(),運行結果如下
可以總結出以下兩個函數:
- getMessage():獲取一些描述性信息
- printStackTrace():從方法調用出到異常拋出處的方法調用序列
重新拋出異常
有時希望把捕獲的異常重新拋出,在catch中已經得到了對當前異常對象的引用,可以將其重新拋出。
public class ExceptionTest {
public static void g() {
throw new MyException("Originated in g()");
}
public static void h() {
try {
g();
} catch (MyException e) {
System.out.println("An Exception was thrown from g()");
throw e;
}
}
public static void k() throws Exception {
try {
g();
} catch (MyException e) {
System.out.println("An Exception was thrown from g()");
throw (Exception) e.fillInStackTrace();
}
}
public static void main(String[] argv) throws Exception {
try {
h();
} catch (MyException e) {
System.out.println(e.getMessage());
e.printStackTrace(System.out);
}
System.out.println();
try {
k();
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace(System.out);
}
}
}
從運行結果可以看出,如果只是將異常拋出,那么printStackTrace()顯示的仍是原來拋出點的調用棧信息,并非重新拋出點的信息,若想更新信息,可以調用
fillInStackTrace()方法,它通過把當前調用棧信息填入原來那個異常對象而建立。這樣調用fillInStackTrace()的那一行就會新的異常發生點。
使用finally清理
在java中finally的存在并不是為了釋放內存資源,因為java有垃圾回收機制,因此需要java釋放的資源主要是:已經打開的文件或網絡連接等。
在try中無論有沒有捕獲異常,finally都會被執行。