標籤:程式員 異常 java
——Java培訓、Android培訓、iOS培訓、.Net培訓、期待與您交流! ——-
異常的介紹Java中的異常就是那些會阻礙當前程式運行,使程式執行可能失敗的一些可能情況,如程式中出現除零錯誤,數組下標越界等。異常在Java中被封裝成了一個類,繼承自
Throwable
,名為
Exception
,它有很多子類,分別描述了系統中很多常見的異常情況,這些異常機制的出現使得編寫程式時對一些問題的處理變得尤為方便,下面是一些簡單的使用方式。異常捕獲的一般格式
/** * javac ExceptionDemo.javac * java ExceptionDemo * 輸出:String index out of range: 3 */class ExceptionDemo { public static void main(String[] args) { try { // 可能產生異常的代碼放到try內 System.out.println("Hi".charAt(3)); } catch(Exception e) { // 對異常的處理 System.out.println(e.getMessage()); } }}
當將
catch
中的
Exception
換成
StringIndexOutOfBoundsException
時,輸出結果同樣為
String index out of range: 3
,如下:這裡
Exception
是
StringIndexOutOfBoundsException
的父類,子類的對象賦值給父類的類型,這在Java中稱作多態。
/** * 輸出:String index out of range: 3 */class ExceptionDemo { public static void main(String[] args) { try { // 可能產生異常的代碼放到try內 System.out.println("Hi".charAt(3)); } catch(StringIndexOutOfBoundsException e) { // 對異常的處理 System.out.println(e.getMessage()); } }}
異常的繼承關係從
Exception
的繼承關係圖可以清楚的知道
Exception
的父類及其子類的關係,超類是
Throwable
(
Error
和
Exception
的共同超類),
Throwable
的超類便是
Object
(除本身外Java中所有類的直接或者間接超類)。以
Exception
結尾的類都是繼承自
Exception
。
當
try
內有多條語句時,可能會產生多種異常,下列代碼雖然加上了異常捕獲操作但是沒有還是產生了異常,程式崩潰。
/** * 輸出:H * Exception in thread "main" java.lang.ArithmeticException: / by zero * at ExceptionDemo3.main(ExceptionDemo3.java:10) */class ExceptionDemo3 { public static void main(String[] args) { try { // 可能產生異常的代碼放到try內 System.out.println("Hi".charAt(0)); System.out.println(123 / 0); } catch(StringIndexOutOfBoundsException e) { // 對異常的處理 System.out.println(e.getMessage()); } }}
原因是雖然有catch來捕獲異常,但是僅僅捕獲的是
StringIndexOutOfBoundsException
異常,這條異常只有
System.out.println("Hi".charAt(0));
才會產生。而
System.out.println(123 / 0);
這條語句會產生另一種異常,叫做
ArithmeticException
,即運算異常。而這條異常沒有相應的捕獲語句,所以虛擬機器採用預設處理方式,即讓程式崩潰。加上
ArithmeticException
異常捕獲後便可以正常處理異常,如下:
/** * 輸出:H * 異常:/ by zero */class ExceptionDemo3 { public static void main(String[] args) { try { // 可能產生異常的代碼放到try內 System.out.println("Hi".charAt(0)); System.out.println(123 / 0); } catch(StringIndexOutOfBoundsException e) { // 對異常的處理 System.out.println(e.getMessage()); } catch(ArithmeticException e) { System.out.println("異常:" + e.getMessage()); } }}
難道try內有大量語句時,會產生很多異常的情況就要加很多個
catch
?當然你可以像第一個樣本那樣使用
Exception
來接收所有異常,但是這樣又會有問題,那就是所有異常都會統一處理,那麼就使用
Exception
和其他異常混合使用的情況,這種情況的時候要注意一點,
Exception
一定要放在最後面一條
catch
中,否則編譯會報錯。正確寫法如下:
/** * 輸出:H * 異常:/ by zero */class ExceptionDemo3 { public static void main(String[] args) { try { // 可能產生異常的代碼放到try內 System.out.println("Hi".charAt(0)); System.out.println(123 / 0); } catch(StringIndexOutOfBoundsException e) { // 對異常的處理 System.out.println(e.getMessage()); } catch(ArithmeticException e) { System.out.println("異常:" + e.getMessage()); } catch(Exception e) { System.out.println(e.getMessage()); } }}
finally
關鍵字Java異常捕獲中的另一個關鍵字
finally
,同
catch
的用法類似,不過
finally
後沒有類型,
finally
的功能是作為
try...catch...finally
中必然執行的一段。也就是說
try
內的代碼產生或者沒有產生異常,最終都會執行
finally
內的代碼。這可以應用到一些網路操作中,如訪問資料庫時有開啟資料庫,當操作資料庫時出現了錯誤,那麼在
finally
中寫上關閉資料庫的操作便起到了很好的作用。避免系統開啟很多資料庫連接而無法關閉且又無法操作,這樣會非常消耗系統資源。訪問網路時也是同樣的道理。一些
finally
的示範如下:
/** * 輸出: * B * C */class ExceptionDemo4 { public static void main(String[] args) { try { int num = 4 / 0; // 製作異常 System.out.println("A"); } catch (Exception e) { System.out.println("B"); } finally { System.out.println("C"); } }}/** * 輸出: * A * B * C */class ExceptionDemo4 { public static void main(String[] args) { try { System.out.println("A"); int num = 4 / 0; // 製作異常 } catch (Exception e) { System.out.println("B"); } finally { System.out.println("C"); } }}/** * 輸出: * 4 * 4 * 0 */class ExceptionDemo4 { public static void main(String[] args) { int num = 4; try { System.out.println(num); int n = 10 / 0; // 製造異常 num += 2; // 異常發生後會立即進入異常處理部分 } catch (Exception e) { System.out.println(num); num = 0; } finally { System.out.println(num); } }}
try
內的代碼在執行時碰到了異常後便不再繼續執行,而是跳到對應的異常處理程式碼片段執行,然後再執行
finally
段的代碼。在帶有傳回值的函數中時,
finally
的執行如下:
/** * 輸出: * try:4 * catch:4 * finally:5 * main:4 */class ExceptionDemo5 { public static void main(String[] args) { System.out.println("main:" + method()); } public static int method() { int num = 4; try { System.out.println("try:" + num); int n = 10 / 0; // 製造異常 } catch (Exception e) { System.out.println("catch:" + num); return num; } finally { num ++; System.out.println("finally:" + num); } return 0; }}
當
finally
之前出現了
return
語句時,傳回值的內容會被壓棧,所以在
finally
中修改
num
的值是不會影響最終在
main
函數中接收到的傳回值的內容的,這也體現了
finally
一定會執行的一點。但是當遇到下面這種情況就要另當別論了:
import java.io.File;import java.io.FileOutputStream;import java.io.IOException;/** * 檔案內容為:Hello * 若將System.exit(0);注釋掉,檔案內容為Hi */class ExceptionDemo5 { public static void main(String[] args) { try { FileOutputStream fout = new FileOutputStream(new File("E:\\ex.txt")); fout.write("Hello".getBytes()); fout.close(); System.exit(0); // 直接退出系統 } catch (IOException e) { } finally { try { FileOutputStream fout = new FileOutputStream(new File("E:\\ex.txt")); fout.write("Hi".getBytes()); fout.close(); } catch (IOException e) { } } }}
所以
finally
的內容必然執行也是要建立再程式還處於正在啟動並執行狀態,程式已退出虛擬機器當然無法再執行
finally
的內容了。
try...finally
組合
除了try...catch...finally
還有一種try...finally
組合方式,即去掉catch段。若程式碼片段拋出的是RuntimeException
便將異常拋給上一層,若是非RuntimeException
或其子類便會編譯錯誤。(ArithmeticException
屬於RuntimeException
)
/** * 輸出: * method:finally * main:/ by zero */class ExceptionDemo6 { public static void main(String[] args) { try { method(); } catch(Exception e) { System.out.println("main:" + e.getMessage()); } } public static void method() { try { int num = 6 / 0; } finally { System.out.println("method:finally"); } }}
異常的拋出
throws
對於異常的處理可以使用
try...catch
,可以使用
try...catch...finally
,也可以使用
try..finally
,當然還有其他方式,你可以使用
throws
把異常拋給上一層,這裡的上一層指的是如
main
函數調用
method
方法,對於
method
方法來說
main
函數就是上一層。
/** * 輸出: * main:Hello */class ExceptionDemo7 { public static void main(String[] args) { try { method(); } catch(ClassNotFoundException e) { System.out.println("main:" + e.getMessage()); } } public static void method() throws ClassNotFoundException { Class<?> c = Class.forName("Hello"); }}
多個異常的拋出時可以在
throws
後面使用
,
隔開,而拋出一條異常是使用
throw
來實現的,如下:
/** * 輸出: * main:-Message- */class ExceptionDemo7 { public static void main(String[] args) { try { method(); } catch(ClassNotFoundException e) { System.out.println("main:" + e.getMessage()); } catch(Exception e) { System.out.println("main:" + e.getMessage()); } } public static void method() throws ClassNotFoundException, IllegalAccessException { // Class<?> c = Class.forName("Hello"); throw(new IllegalAccessException("-Message-")); }}
自訂異常自訂異常其實很簡單,只需要繼承
Exception
即可,或者繼承
Exception
的子類,如下:
/** * 輸出: * main:My Exception */class MyException extends Exception { public MyException(String msg) { super(msg); }}class ExceptionDemo7 { public static void main(String[] args) { try { method(); } catch(MyException e) { System.out.println("main:" + e.getMessage()); } } public static void method() throws MyException { throw(new MyException("My Exception")); }}
RuntimeException
簡介當拋出
RuntimeException
或者
RuntimeException
的子類時無需使用
throws
在方法名後面標出,這一類異常可能無法通過捕獲處理很好地處理,如除零錯誤發生時即使捕獲到了異常,但是後面的運算結果是會受到影響的,一定會出現一個不正確的運算結果。如上面出現過的除零異常,無需在方法名後加異常類型說明:
/** * 輸出: * method:finally * main:/ by zero */class ExceptionDemo6 { public static void main(String[] args) { try { method(); } catch(Exception e) { System.out.println("main:" + e.getMessage()); } } public static void method() /*這裡不需要加 ArithmeticException*/{ try { int num = 6 / 0; } finally { System.out.println("method:finally"); } }}
黑馬程式員-Java異常詳解