目錄java
前言:Java 中的異常處理是處理程序運行錯誤時的強大機制之一,它能夠保證應用程序的正常流程。程序員
首先咱們將瞭解java異常、異常的類型以及受查和非受查異常之間的區別。數組
字面意義:異常是一種不正常的狀況。安全
在 java 中,異常是擾亂程序正常流程的事件,它是在程序運行時拋出的對象。學習
異常處理一種在運行時解決程序錯誤的機制,例如 ClassNotFound、IO、SQL、Remote 等。3d
異常一般會干擾程序的正常流程,而異常處理的核心優點是維護程序的正常流程。如今讓咱們假設一下:指針
statement 1; statement 2; statement 3; statement 4; statement 5;//發生異常 statement 6; statement 7; statement 8; statement 9; statement 10;
假設你的程序中有10條語句,若是在第5條中出現了一個異常,那麼語句6-10將不會繼續執行。若是你使用了異常處理,那麼語句6-10的部分將正常執行,這就是咱們爲何須要在程序中使用異常處理的緣由。調試
你知道嗎?code
- 受查和非受查異常之間的區別是什麼?
- 代碼
int data=50/0;
後面發生了什麼?- 爲何須要使用多個
catch
塊?finally
塊是否有可能不執行?- 什麼是異常傳遞?
throw
和throws
關鍵字之間的區別?- 對方法重寫使用異常處理的4條規則是什麼?
如今讓咱們帶着以上問題繼續下面的學習。orm
主要有兩種類型的異常:受查和非受查異常,Error
被視爲非受查異常。Sun公司認爲有三種異常類型:
1)受查異常
除了RuntimeException
和Error
外,繼承自Throwable
類的類稱爲受查異常,例如:IOException、SQLException 等。受查異常在編譯時進行檢查。
常見的有如下幾個方面:
2)非受查異常
繼承自RuntimeException
類的異常被稱爲非受查異常,例如:ArithmeticException、 NullPointerException、 ArrayIndexOutOfBoundsException 等。非受查異常不會在編譯時檢查,而是在運行時進行檢查。
常見的有如下幾個方面:
「若是出現了RuntimeException
異常,那麼必定是你自身的問題」,是一條至關有道理的規則。
3)錯誤(Error)
錯誤是一種沒法恢復的異常類型,一般是在java運行時系統的內部錯誤和資源耗盡錯誤。應用程序不該該拋出這種類型的對象。若是出現了這樣的內部錯誤,除了通告給用戶,並盡力的使得程序安全的終止以外,再也無能爲力了。這種狀況不多出現。
在某些狀況下,可能出現未檢查的異常,它們以下:
1)發生ArithmeticException
的場景
若是咱們將任何數字除以0,就會出現一個 ArithmeticException 異常。
int a = 50/0;//ArithmeticException
2)發生NullPointerException
的場景
若是變量的值爲null
,那麼調用此變量將會出現 NullPointerException 異常。
String s = null; System.out.println(s.length());//NullPointerException
3)發生NumberFormatException
的場景
任何值的格式錯誤,都有肯能發生 NumberFormatException 異常。假設一個字符串變量,其中包含了字符,若將此變量轉換爲數字類型,將會發生 NumberFormatException 異常。
String s = "abc"; int i = Integer.parseInt(s);//NumberFormatException
4)發生ArrayIndexOutOfBoundsException
的場景
若是你在一個不存在的的數組索引中插入任何值,則會致使 ArrayIndexOutOfBoundsException 異常。
int a[] = new int[5]; a[10] = 50; //ArrayIndexOutOfBoundsException
下面是 Java 異常處理中的5個關鍵字:
try
、catch
、finally
、throw
、throws
在程序中,可能會遇到任何標準異常類都沒有可以充分地描述清楚的問題。在這種狀況下,建立本身的異常類就是一件瓜熟蒂落的事情了。咱們須要作的只是定義一個派生於 Exception 的類,或者派生於 Exception 子類的類。例如,定義一個派生於 IOException 的類。
習慣上,定義的類應該包含兩個構造器,一個是默認構造器,一個是描述詳細信息的的構造器(超類 Throwable 的 toString 方法將會打印出這些詳細信息,這在調試中很是有用。)
示例以下:
class FileFormatException extends IOException { public FileFormatException() {} public FileFormatException(String gripe) { super(gripe); } }
如今,就能夠拋出本身定義的異常類型了。
String readData(BufferedReader in) throws FileFormatException { ... while (...) { // EOF encountered if (ch == -1) { if (n < len) throw new FileFormatException(); } ... } return s; }
將可能發生異常的代碼放在try
塊中,且必須在方法中才能使用。try 塊後必須使用catch
塊或finally
塊。
1)try-catch 語法
try{ // 可能拋出異常的代碼 }catch(Exception_class_Name ref){}
2)try-finally 語法
try{ // 可能拋出異常的代碼 }finally{}
Java catch
塊被用於處理異常,必須在try
塊後使用。
你能夠在一個try
塊後使用多個catch
塊
若是咱們不使用try-catch
處理異常,看看會發生什麼。
public class Testtrycatch1 { public static void main(String args[]) { int data=50/0;// 可能拋出異常 System.out.println("代碼的其他部分..."); } }
輸出:
Exception in thread main java.lang.ArithmeticException:/ by zero
如上面的示例所示,代碼的其他部分並無執行。("代碼的其他部分..."未打印)
讓咱們經過try-catch
塊來查看上述問題的解決方案。
public class Testtrycatch2 { public static void main(String args[]) { try { int data = 50/0; } catch(ArithmeticException e) { System.out.println(e); } System.out.println("代碼的其他部分..."); } }
輸出:
Exception in thread main java.lang.ArithmeticException:/ by zero 代碼的其他部分...
如今,正如上面的示例所示,代碼的其他部分執行了.(也就是"代碼的其他部分..."被打印)
Java 虛擬機首先檢查異常是否被處理,若是異常未處理,則執行的一個默認的異常處理程序:
若是程序員處理了異常,則應用程序按照正常流程執行。
若是須要在發生不一樣異常時執行不一樣的任務,則須要使用多個 catch 塊。
查看下面一個簡單的多 catch 塊示例。
public class TestMultipleCatchBlock{ public static void main(String args[]) { try{ int a[] = new int[5]; a[5] = 30/0; } catch(ArithmeticException e) { System.out.println("任務1已完成"); } catch(ArrayIndexOutOfBoundsException e) { System.out.println("任務2已完成"); } catch(Exception e) { System.out.println("已完成通用任務"); } System.out.println("代碼的其他部分..."); } }
輸出:
任務1已完成 代碼的其他部分...
規則:一次只有一個異常發生,而且一次只執行一個catch塊。
規則: 全部異常必須從最具體到最通用的順序排序,即捕獲
ArithmeticException
必須在捕獲Exception
以前發生。
class TestMultipleCatchBlock1 { public static void main(String args[]) { try{ int a[]=new int[5]; a[5]=30/0; } catch(Exception e) { System.out.println("已完成通用任務"); } catch(ArithmeticException e) { System.out.println("任務1已完成"); } catch(ArrayIndexOutOfBoundsException e) { System.out.println("任務2已完成"); } System.out.println("代碼的其他部分..."); } }
輸出:
Compile-time error
Java try塊中的try塊被稱爲try嵌套塊。
有時可能會出現一種狀況,一個塊的某個部分可能致使一個錯誤,而整個塊的自己可能會致使另外一個錯誤。在這種狀況下,必須使用嵌套異常處理程序。
語法:
.... try { statement 1; statement 2; try { statement 1; statement 2; } catch(Exception e) { ... } } catch(Exception e) {...} ....
class Excep6 { public static void main(String args[]) { try { // try 嵌套塊1 try { System.out.println("try 嵌套塊1"); int b = 39 / 0; } catch(ArithmeticException e) { System.out.println(e); } // try 嵌套塊2 try { int a[] = new int[5]; a[5] = 4; } catch(ArrayIndexOutOfBoundsException e) { System.out.println(e); } System.out.println("try外部塊其餘語句..."); } catch(Exception e) { System.out.println("handeled"); } System.out.println("正常流..."); } }
輸出:
try 嵌套塊1 java.lang.ArithmeticException: / by zero java.lang.ArrayIndexOutOfBoundsException: 5 try外部塊其餘語句... 正常流...
Java finally 塊是用來執行重要代碼的塊(如關閉鏈接、流等)。
不管是否處理異常,最終都會執行 finally 塊。
finally 塊緊跟 try 或 catch 塊後:
注意:不管你是否處理異常,在終止程序以前,JVM都將執行finally塊(若是存在的話)
finally 塊能夠用於放置"clear"代碼,例如關閉文件,關閉鏈接等。
接下來讓咱們來看看在不一樣狀況下使用 finally 塊。
1)案例1
當前沒有發生異常:
class TestFinallyBlock { public static void main(String[] args) { try { int data = 25 / 5; System.out.println(data); } catch (NullPointerException e) { System.out.println(e); } finally { System.out.println("finally 塊老是執行"); } System.out.println("代碼的其他部分..."); } }
輸出:
5 finally 塊老是執行 代碼的其他部分...
2)案例2
發生異常但未處理:
class TestFinallyBlock1 { public static void main(String[] args) { try { int data = 25 / 0; System.out.println(data); } catch (NullPointerException e) { System.out.println(e); } finally { System.out.println("finally 塊老是執行"); } System.out.println("代碼的其他部分..."); } }
輸出:
finally 塊老是執行 Exception in thread main java.lang.ArithmeticException:/ by zero
3)案例3
發生異常並處理異常:
public class TestFinallyBlock2 { public static void main(String args[]) { try { int data = 25 / 0; System.out.println(data); } catch(ArithmeticException e) { System.out.println(e); } finally { System.out.println("finally 塊老是執行"); } System.out.println("代碼的其他部分..."); } }
輸出:
Exception in thread main java.lang.ArithmeticException:/ by zero finally 塊老是執行 代碼的其他部分...
規則:對於 try 塊能夠有0個或多個 catch 塊,但僅僅只能有一個 finally 塊。
規則:若是程序退出(經過調用 System.exit() 或經過致使進程停止的致命錯誤),finally塊將不會被執行。
Java throw 關鍵字用於顯示的拋出異常。
咱們可使用 throw 關鍵字在 Java 中拋出檢查(Checked)或未檢查(UnChecked)異常。throw 關鍵字主要用於拋出自定義異常。
Java throw 語法以下:
throw exception;
拋出IOException
異常的例子:
throw new IOException("sorry device error");
在本例中,咱們建立了一個將整數值做爲參數的 validate 方法。若是年齡小於18歲,咱們將拋出一個ArithmeticException
異常,不然打印一條消息"歡迎投票"。
public class TestThrow1 { static void validate(int age) { if(age < 18) throw new ArithmeticException("無效"); else System.out.println("歡迎投票"); } public static void main(String args[]) { validate(13); System.out.println("代碼的其他部分..."); } }
輸出:
Exception in thread main java.lang.ArithmeticException:無效
異常首先從堆棧頂部拋出,若是未捕獲,則將調用堆棧降低到前一個方法,若是沒有捕獲,則將異常再次降低到先前的方法,以此類推,知道它們被捕獲或到達調用堆棧底部爲止。以上稱爲異常傳遞。
規則:默認狀況下,非受查異常在調用鏈中(傳遞)轉發。
異常傳遞示例:
class TestExceptionPropagation1 { void m(){ int data = 50 / 0; } void n() { m(); } void p() { try{ n(); } catch(Exception e) { System.out.println("異常處理器"); } } public static void main(String args[]) { TestExceptionPropagation1 obj = new TestExceptionPropagation1(); obj.p(); System.out.println("正常流..."); } }
輸出:
異常處理器 正常流...
在上面的示例中。異常發生在 m() 方法中,若是未對其進行處理,則將其傳遞到未處理它的前 n() 方法,再次將其傳遞處處理異常的 p() 方法。
能夠在 main()、p()、n()、p()、 m() 中的任何方法中處理異常。
規則:默認狀況下,受查異常不會在調用鏈中(傳遞)轉發。
用於描述受查異常不會在程序中傳遞的示例:
class TestExceptionPropagation2{ void m(){ throw new java.io.IOException("設備異常"); // 受查異常 } void n(){ m(); } void p(){ try{ n(); } catch(Exception e){ System.out.println("異常處理器"); } } public static void main(String args[]){ TestExceptionPropagation2 obj=new TestExceptionPropagation2(); obj.p(); System.out.println("正常流..."); } }
輸出:
Compile Time Error
編譯時發生一個錯誤,證實受查異常並不會在程序中進行傳遞。
Java throws
關鍵字被用於聲明一個異常。它給程序員提供了一個信息,說明可能會發生異常,因此程序員最好提供異常處理代碼,以保證程序正常的流程。
異常處理主要用於處理受查異常,若是出現任何非受查異常,如"NullPointerException",都是程序員自身的錯誤,請認真檢查你的代碼。
return_type method_name() throws exception_class_name { // method code }
僅僅聲明受查異常,由於:
VirtualMachineError
或 StackOverflowError
等異常,將沒法進行任何操做。使用 throws 聲明受查異常後,使得受查異常能夠在調用堆棧中進行(傳遞)轉發。它向處理該異常的方法提供異常信息。
下面的示例描述了受查異常能夠經過throws
關鍵字進行傳遞:
import java.io.IOException; class Testthrows1{ void m() throws IOException{ throw new IOException("設備異常"); // 受查異常 } void n()throws IOException{ m(); } void p(){ try{ n(); } catch(Exception e){ System.out.println("異常處理器"); } } public static void main(String args[]){ Testthrows1 obj=new Testthrows1(); obj.p(); System.out.println("正常流..."); } }
輸出:
異常處理器 正常流...
規則:若是你正在調用一個聲明瞭異常的方法,則必須捕獲或聲明異常。
如今有兩種狀況:
1) 狀況1:處理了異常
import java.io.*; class M{ void method() throws IOException{ throw new IOException("設備異常"); } } public class Testthrows2{ public static void main(String args[]){ try{ M m = new M(); m.method(); } catch(Exception e){ System.out.println("異常處理器"); } System.out.println("正常流..."); } }
輸出:
異常處理器 正常流...
2) 狀況2:聲明瞭異常
A)聲明瞭異常但未發生異常:
import java.io.*; class M{ void method()throws IOException{ System.out.println("執行設備操做"); } } class Testthrows3{ public static void main(String args[])throws IOException{ // 聲明瞭異常 M m=new M(); m.method(); System.out.println("正常流..."); } }
輸出:
執行設備操做 正常流...
B)聲明瞭異常且發生了異常:
import java.io.*; class M{ void method()throws IOException{ throw new IOException("設備錯誤"); } } class Testthrows4{ public static void main(String args[])throws IOException{ // 聲明瞭異常 M m=new M(); m.method(); System.out.println("正常流..."); } }
輸出:
Runtime Exception
程序編譯時將直接出現了一個編譯錯誤。
No. | throw | throws |
---|---|---|
1) | Java throw 關鍵字用於顯示的拋出異常 | Java throws 關鍵字用於聲明一個異常 |
2) | 受查異常不能只使用 throw 進行傳遞 | 受查異常能夠經過 throws 進行傳遞 |
3) | Throw 後面跟着一個異常實例 | Throws 後面跟着一個異常類 |
4) | 在方法中使用 Throw | Throws 與方法簽名一塊兒使用 |
5) | 你不能拋出多個異常 | 你能夠聲明多個異常,例如public void method() throws IOException,SQLException |
1)Java throw 示例:
void m(){ throw new ArithmeticException("sorry"); }
2)Java throws 示例:
void m()throws ArithmeticException{ // method code }
3)Java throw 和 throws 示例:
void m()throws ArithmeticException{ throw new ArithmeticException("sorry"); }
答案固然是能夠的,能夠在 catch 塊中拋出相同的異常。這種方法一般用於只想記錄一個異常,但不作任何改變。
代碼示例:
try { // access the database } catch (Exceptiom e) { logger.log(level, message, e); throw e; }
在 Java SE7 以前,這種方法存在一個問題,假設這段代碼在如下方法中:
public void updateRecord() throws SQLException
Java 編譯器查看 catch 塊中的 throw 語句,而後查看 e 的類型,會指出這個方法能夠拋出任何 Exception 而不只僅是 SQLException。如今這個問題已經有所改進,編譯器會追蹤到 e 來自 try 塊。假設這個 try 塊僅有的受查異常是 SQLException 實例,另外,假設 e 在 catch 塊中未改變,將外圍方法聲明爲 throws SQLException 是合法的。
Final 和 Finally 和 Finalize 三者之間的差別以下:
No. | final | finally | finalize |
---|---|---|---|
1) | final 用於對類、方法和變量加以限制,final 類不能被繼承,final 方法不能被重寫,final 變量不能被更改 | finally 用於放置重要的代碼,不管異常是否被處理它都會執行 | finalize 用於在對象被垃圾回收以前執行清理操做 |
2) | final 是一個關鍵字 | finally 是一個塊 | finalize 是一個方法 |
1)Java final 示例:
class FinalExample{ public static void main(String[] args){ final int x = 100; x = 200; // final 修飾的變量不能被更改 // 編譯時將出錯 } }
2)Java finally 示例:
class FinallyExample{ public static void main(String[] args){ try{ int x = 300; } catch(Exception e){ System.out.println(e); } finally{ System.out.println("finally 塊始終被執行"); } } }
3)Java finalize 示例:
class FinalizeExample{ public void finalize(){ System.out.println("finalize called"); } public static void main(String[] args){ FinalizeExample f1 = new FinalizeExample(); FinalizeExample f2 = new FinalizeExample(); f1 = null; f2 = null; System.gc(); } }
關於重寫異常處理方法的規則以下:
1)若是超類方法沒有聲明異常
超類方法未聲明異常,子類重寫方法聲明受查異常的示例:
import java.io.*; class Parent{ void msg(){ System.out.println("parent"); } } class TestExceptionChild extends Parent{ void msg() throws IOException{ System.out.println("Child"); } public static void main(String args[]){ Parent p = new TestExceptionChild(); p.msg(); } }
輸出:
Compile Time Error
超類方法未聲明異常,子類重寫方法聲明非受查異常的示例:
import java.io.*; class Parent{ void msg(){ System.out.println("parent"); } } class TestExceptionChild1 extends Parent{ void msg() throws ArithmeticException{ System.out.println("child"); } public static void main(String args[]){ Parent p = new TestExceptionChild1(); p.msg(); } }
輸出:
child
2)若是超類方法聲明瞭異常
A)超類方法聲明瞭異常,子類重寫方法聲明不相同父類異常的示例:
import java.io.*; class Parent{ // 聲明瞭子類異常 void msg() throws ArithmeticException{ System.out.println("parent"); } } class TestExceptionChild2 extends Parent{ // 聲明瞭父類異常 void msg() throws Exception{ System.out.println("child"); } public static void main(String args[]){ Parent p = new TestExceptionChild2(); try{ p.msg(); } catch(Exception e){ } } }
輸出:
Compile Time Error
B)超類方法聲明瞭異常,子類重寫方法聲明相同異常的示例:
import java.io.*; class Parent{ void msg()throws Exception{ System.out.println("parent"); } } class TestExceptionChild3 extends Parent{ void msg()throws Exception{ System.out.println("child"); } public static void main(String args[]){ Parent p=new TestExceptionChild3(); try{ p.msg(); } catch(Exception e){ } } }
輸出:
child
C)超類方法聲明瞭異常,子類重寫方法聲明不相同子類異常的示例:
import java.io.*; class Parent{ // 聲明瞭父類異常 void msg()throws Exception{ System.out.println("parent"); } } class TestExceptionChild4 extends Parent{ // 聲明瞭子類異常 void msg()throws ArithmeticException{ System.out.println("child"); } public static void main(String args[]){ Parent p=new TestExceptionChild4(); try{ p.msg(); } catch(Exception e){ } } }
輸出:
child
D)超類方法聲明瞭異常,子類重寫方法未聲明異常的示例:
import java.io.*; class Parent{ void msg()throws Exception{ System.out.println("parent"); } } class TestExceptionChild5 extends Parent{ void msg(){ System.out.println("child"); } public static void main(String args[]){ Parent p=new TestExceptionChild5(); try{ p.msg(); } catch(Exception e){ } } }
輸出:
child
參考文章:https://www.javatpoint.com/exception-handling-in-java 參考書籍:《Java核心技術 卷1》