Java異常機制深入理解
在程式執行過程中出現錯誤,導致程式出現非預期場景。異常處理可以保證出現錯誤後,控制接下來的程式流程,是選擇定位錯誤資訊,還是丟擲異常或捕獲異常、還是避免程式非正常退出,都取決於我們。 |
異常:程式在執行過程中發生由於外部問題導致的程式異常事件,發生的異常會中斷程式的執行。(在Java等物件導向的程式語言中)異常本身是一個物件,產生異常就是產生了一個異常物件。注意在java中異常不是錯誤,在下文的異常的分類中有解釋。
舉個生活中的栗子來說明異常吧,假如你平時是開車上班,一般情況下你都能按時到公司上班,但是今天遇到特殊情況,就是遇到公路施工了,遇到這種情況,如果你沒有做任何處理,就有可能導致上班遲到。這種“路上施工” 就是程式中的異常了,他是外部問題導致的事件,並不是你本身出現的問題。(題外話:然而老闆並不在意你是什麼問題導致遲到的,反正你就是遲到了。。。。)
1.2.1 傳統的異常處理
假如現在要求在控制檯中,輸入被除數和除數,求商。
傳統做法是這樣的:
public static void main(String[] args) { System.out.println("請輸入一個被除數:"); Scanner sc = new Scanner(System.in); if(sc.hasNextInt()) { int num1 = sc.nextInt(); System.out.println("請輸入一個除數:"); if(sc.hasNextInt()) { int num2 = sc.nextInt(); if(0 == num2) { System.out.println("除數不能為0!"); }else { int r = num1 / num2; System.out.println("r = " + r); } }else { System.out.println("除數輸入不合法!"); } }else { //在控制檯有可能輸入字串 System.out.println("被除數輸入不合法!"); } }
從上面這個例子可以可以看出,這麼簡單的業務需求,程式碼也要寫得那麼長,因為要考慮的問題有很多,這樣寫程式碼會覺得很累,而且出現了異常,程式會中斷,不會執行後面的程式碼。因此,Java程式語言使用異常處理機制為程式提供異常處理的能力。
1.2.2 java的異常處理
在Java中,異常處理的過程:
這種處理過程就像你上班遇到公路施工,你做出了處理——繞路行走,避開施工路段,讓你按時到達公司!
在 Java 中,所有的異常都有一個共同的祖先 Throwable(可丟擲)。Throwable 指定程式碼中可用異常傳播機制透過 Java 應用程式傳輸的任何問題的共性。
Throwable: 有兩個重要的子類:Exception(異常)和 Error(錯誤),二者都是 Java 異常處理的重要子類,各自都包含大量子類。
Error(錯誤):是程式無法處理的錯誤,表示執行應用程式中較嚴重問題。大多數錯誤與程式碼編寫者執行的操作無關,而表示程式碼執行時 JVM(Java 虛擬機器)出現的問題。例如,Java虛擬機器執行錯誤(Virtual MachineError),當 JVM 不再有繼續執行操作所需的記憶體資源時,將出現 OutOfMemoryError。這些異常發生時,Java虛擬機器(JVM)一般會選擇執行緒終止。
這些錯誤表示故障發生於虛擬機器自身、或者發生在虛擬機器試圖執行應用時,如Java虛擬機器執行錯誤(Virtual MachineError)、類定義錯誤(NoClassDefFoundError)等。這些錯誤是不可查的,因為它們在應用程式的控制和處理能力之 外,而且絕大多數是程式執行時不允許出現的狀況。對於設計合理的應用程式來說,即使確實發生了錯誤,本質上也不應該試圖去處理它所引起的異常狀況。在 Java中,錯誤透過Error的子類描述。
Exception(異常):是程式本身可以處理的異常。Exception 類有一個重要的子類 RuntimeException。RuntimeException 類及其子類表示“JVM 常用操作”引發的錯誤。例如,若試圖使用空值物件引用、除數為零或陣列越界,則分別引發執行時異常(NullPointerException、ArithmeticException)和 ArrayIndexOutOfBoundException。注意:異常和錯誤的區別:異常能被程式本身可以處理,錯誤是無法處理。通常,Java的異常(包括Exception和Error)分為可查的異常(checked exceptions)和不可查的異常(unchecked exceptions)。
可查異常(編譯器要求必須處置的異常):正確的程式在執行中,很容易出現的、情理可容的異常狀況。可查異常雖然是異常狀況,但在一定程度上它的發生是可以預計的,而且一旦發生這種異常狀況,就必須採取某種方式進行處理。
除了RuntimeException及其子類以外,其他的Exception類及其子類都屬於可查異常。這種異常的特點是Java編譯器會檢查它,也就是說,當程式中可能出現這類異常,要麼用try-catch語句捕獲它,要麼用throws子句宣告丟擲它,否則編譯不會透過。
不可查異常(編譯器不要求強制處置的異常):包括執行時異常(RuntimeException與其子類)和錯誤(Error)。
Exception 這種異常分兩大類執行時異常和非執行時異常(編譯異常)。程式中應當儘可能去處理這些異常。
執行時異常:都是RuntimeException類及其子類異常,如NullPointerException(空指標異常)、IndexOutOfBoundsException(下標越界異常)等,這些異常是不檢查異常,程式中可以選擇捕獲處理,也可以不處理。這些異常一般是由程式邏輯錯誤引起的,程式應該從邏輯角度儘可能避免這類異常的發生。執行時異常的特點是Java編譯器不會檢查它,也就是說,當程式中可能出現這類異常,即使沒有用try-catch語句捕獲它,也沒有用throws子句宣告丟擲它,也會編譯透過。
非執行時異常 (編譯異常):是RuntimeException以外的異常,型別上都屬於Exception類及其子類。從程式語法角度講是必須進行處理的異常,如果不處理,程式就不能編譯透過。如IOException、SQLException等以及使用者自定義的Exception異常,一般情況下不自定義檢查異常。
透過上面對異常的解釋,現在應該對異常有一定的瞭解。下面來說明在java中是如何處理異常的。
•在java中用物件來表示異常的。
•java是透過try-catch、try-catch-finally、try-catch-catch...語句來處理異常的。
3.1.1 try-catch的使用
try{} 程式碼塊用於執行可能存在異常的程式碼,catch(異常型別 異常物件的名稱){}程式碼塊用於捕獲並處理異常。 try{ //有可能出現異常的程式碼段1 //有可能出現異常的程式碼段2 }catch(異常型別 e){ //處理異常的程式碼段3 } //程式碼段4
處理1.2.1的兩數相除問題的demo:
public static void main(String[] args) { Scanner sc = new Scanner(System.in); try{ System.out.println("請輸入一個被除數:"); int num1=sc.nextInt(); System.out.println("請輸入一個除數:"); int num2=sc.nextInt(); int result=num1/num2; }catch(Exception e){ System.out.println("在此處處理異常!"); } System.out.println("程式執行結束"); }
3.1.2 try-catch的執行順序
第一種:沒有遇到異常,即正常執行
第二種:匹配到異常
在try{}中的程式碼遇到異常時,會與catch()中括號裡的異常進行比對,如果遇到的異常時屬於catch的異常就會執行catch塊中的程式碼,讓後執行try-catch塊後面的程式碼
第三種:匹配不到異常
try{} 程式碼塊用於執行可能存在異常的程式碼,catch{}程式碼塊用於捕獲並處理異常。
finally{} 程式碼塊用於回收資源(關閉檔案、關閉資料庫、關閉管道)的程式碼。finally程式碼塊不管是否出現異常,都必須���行( finally塊唯一不執行的情況System.exit(0) jvm正常退出。)
3.1.1 try-catch-finally的執行順序
第一種:catch塊沒有return語句
(1)沒有遇到異常:
try塊內的程式碼——>finally塊內的程式碼——>finally塊後的程式碼
(2)遇到異常並匹配到異常:
try塊內的程式碼——>catch塊內的程式碼——>finally塊內的程式碼——>finally塊後的程式碼
public class Test01 { public static void main(String[] args) { try { int a=1/0; System.out.println("try"); } catch (Exception e) { System.out.println("catch"); }finally { System.out.println("finally"); } System.out.println("程式正常執行結束"); } }
結果:
1 catch
2 finally
3 程式正常執行結束
(3)遇到異常卻沒有匹配到異常:
try塊內的程式碼——>finally塊內的程式碼——>程式中斷執行
public static void main(String[] args) { try { int a=1/0;//會丟擲ArithmeticException System.out.println("try"); } catch (NullPointerException e) { System.out.println("catch"); }finally { System.out.println("finally"); } System.out.println("程式正常執行結束"); }
結果:
1 Exception in thread "main" finally 2 java.lang.ArithmeticException: / by zero 3 at Test1.Test01.main(Test01.java:8)
第二種:catch塊有return語句
(1)沒有異常
try塊內的程式碼——>finally塊內的程式碼——>try塊內的return語句
public class Test01 { public static int test() { try { int a=2*2; System.out.println("try"); return a; } catch (Exception e) { System.out.println("catch"); return 0; }finally { System.out.println("finally"); } } public static void main(String[] args) { System.out.println(test()); System.out.println("程式正常執行結束"); } }
結果:
1 try 2 finally 3 4 4 程式正常執行結束
(2)遇到異常並匹配到異常:
執行順序如圖:
public class Test01 { public static int test() { try { int a=2/0; System.out.println("try"); return a; } catch (Exception e) { System.out.println("catch"); return 0; }finally { System.out.println("finally"); } } public static void main(String[] args) { System.out.println(test()); System.out.println("程式正常執行結束"); } }
結果:
1 catch 2 finally 3 0 4 程式正常執行結束
(3)遇到異常卻沒有匹配到異常:
try塊內的程式碼——>finally塊內的程式碼——>程式中斷執行
public class Test01 { public static int test() { try { int a=2/0; System.out.println("try"); return a; } catch (NullPointerException e) { System.out.println("catch"); return 0; }finally { System.out.println("finally"); } } public static void main(String[] args) { System.out.println(test()); System.out.println("程式正常執行結束"); } }
結果:
finally Exception in thread "main" java.lang.ArithmeticException: / by zero at Test1.Test01.test(Test01.java:8) at Test1.Test01.main(Test01.java:19)
當開發者在定義方法時,事先知道方法在呼叫時會出現異常,但不知道該如何處理,此時可以在該方法上宣告異常。表示該方法在呼叫過程中會出現異常,請呼叫者自行處理。
在java中使用throws 宣告異常。一個方法可以宣告多個異常,用,號分割,寫法如下:
1 public void test2()throws IOException,RuntimeException{ 2 //有異常出得程式碼,在此處沒有處理 3 }
宣告異常和方法過載沒有任何關係。
•如果父類方法宣告瞭異常(檢查時或執行時),子類方法可以完全遵循父類異常,也可以不宣告異常。
•如果父類方法沒有宣告異常,子類可以不宣告異常,也可以宣告RuntimeException,但不能宣告Exception。
•如果父類宣告瞭執行時異常,子類可以完全遵循父類異常,也可以不宣告異常。
當系統異常滿足不了開發需要時,開發者可以自行根據需要自行丟擲異常。
throw 用於手動丟擲異常。
如果一直都沒有處理(即沒有用try-catch等語句)異常會把異常拋給呼叫者,一直拋到main函式處,如果在main函式中也沒有處理繼續在main函式後丟擲異常,這時候會拋給jvm處理。如下栗子:
public class Test01 { public static void test1()throws IOException,RuntimeException{ //有異常丟擲得程式碼,在此處沒有處理,例如:throw new Exception(“異常資訊”); } public static void test2()throws IOException,RuntimeException{ test1();//呼叫有丟擲異常的方法,在此沒有處理 } public static void main(String[] args)throws IOException,RuntimeException { test2();//main呼叫有丟擲異常的方法,在此沒有處理 } }
注意:
開發者根據自身需要可以選擇丟擲檢查時異常和執行時異常
當JDK 中的異常型別不能滿足程式的需要時,可以自定義異常類。
自定義異常步驟:
•[1] 確定異常型別.繼承Excepion 或者RuntimeException
•[2] 編寫自定義異常類,並實現構造方法
•[3] 在方法需要的地方手動宣告並丟擲異常。
public class myException extends Exception { public myException() { super(); } public myException(String message) { super(message); } //自定義異常中的方法,以符合自己的需求 public void showInfo() { System.out.println(super.getMessage()+"@Line:"); } }
原文連結:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31559985/viewspace-2655777/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 深入理解java異常處理機制Java
- 深入理解Java異常Java
- 深入理解java異常處理機制(目前最好的講解異常的博文)Java
- Java異常機制Java
- 深入理解C++中的異常處理機制C++
- Java異常處理機制Java
- java異常的處理機制Java
- Java 的異常處理機制Java
- Java 基礎(十二)異常機制Java
- 深入理解 Java 中 SPI 機制Java
- Java 中的異常處理機制Java
- 深入理解Java序列化機制Java
- [深入理解Java虛擬機器]第二章 實戰 :OutOfMemoryError異常Java虛擬機Error
- 對Java異常的理解Java
- 異常處理機制
- 深入理解Java:類載入機制及反射Java反射
- 深入理解Spring異常處理Spring
- 深入理解Java虛擬機器(類載入機制)Java虛擬機
- 深入理解Java虛擬機器 - 類載入機制Java虛擬機
- 深入理解JVM虛擬機器11:Java記憶體異常原理與實踐JVM虛擬機Java記憶體
- C++ 異常機制(上)C++
- 深入理解DOM事件機制事件
- 深入理解 Kafka 副本機制Kafka
- ORACLE鎖機制深入理解Oracle
- java基礎:深入理解Class物件與反射機制Java物件反射
- 深入理解Java虛擬機器-Java記憶體區域與記憶體溢位異常Java虛擬機記憶體溢位
- 夯實Java基礎系列10:深入理解Java中的異常體系Java
- 08.異常處理機制
- C++異常處理機制C++
- Python異常處理機制Python
- 8.異常處理機制
- 阿里面試題,深入理解Java類載入機制阿里面試題Java
- 深入理解Java的垃圾回收機制(GC)實現原理JavaGC
- 深入理解React:事件機制原理React事件
- Android 深入理解 Notification 機制Android
- 深入理解非同步事件機制非同步事件
- 深入理解 Swift 派發機制Swift
- 用Go語言異常機制模擬TryCatch異常捕捉Go