非常詳細的Java異常處理機制知識整理大全
異常的基本概念:
異常指程序在執(zhí)行過程中出現,程序本身沒有預料的情況,例如讀取文件,操作時文件不存在訪問數據庫,是驅動程序,不存在進行算數,除法運算時除數為零等情情況。
這些情況的出現,可能會導致程序出現不正確的邏輯或者導致程序結束異常,是不可避免的,出現了什么樣的異常,由誰來處理異常,如何處理異常?
傳統面向過程的程序語言,例如C語言,通常根據程序返回的某個特殊值或標記,并且假定接受者會檢查該返回值或標記,以此來判斷異常是否發(fā)生這種處理方式,會在程序的許多地方逐一查某個特定的異常并加以處理,導致正常的業(yè)務流程和異常處理代碼緊密耦合,不利于代碼的閱讀和維護。
相比于其他語言來講,java提供的異常處理機制具有以下優(yōu)點:
1:將描述業(yè)務邏輯的代碼與處理異常的代碼分離,從而使代碼的可讀性撰寫,調試和維護都大大提高。
2:把錯誤傳播給調用堆棧
3:按錯誤類型和錯誤差別分組
3:系統提供了對于一些無法預料的錯誤的捕獲和處理
4:克服了傳統方法的錯誤信息有限的問題
使用異常處理的目的就是用來在發(fā)生異常時高數程序如何控制自身的運行,防止錯誤進一步惡化,從而導致嚴重的后果
Java異常的體系結構:

Throwable是所有異常和錯誤的父類,它主要包含了三個方面的內容:
1:線程創(chuàng)建時執(zhí)行堆棧的快照。
2:用于描述異?;蝈e誤出現位置的消息字符串。
3:異?;蝈e誤產生的原因
Throwable有兩個直接子類:Error和Exception,分別表示錯誤和異常,其中異常Exception又包括兩大類:運行時異常(RuntimeException)和非運行時異常。
運行時異常又稱為編譯器不檢查的異常(Unchecked Exception),非運行時異常又稱為編譯器檢查的異常(checked Exception)
Error與Exception:
Error類層次結構描述了Java運行時系統的內部錯誤和資源耗盡錯誤,例如Out-OfMemoryError(內存溢出錯誤),Java虛擬機不會檢查Error是否被處理,除了通知給用戶并且會盡力使程序安全的終止外,程序本身是無法處理這些錯誤的。
Exception分為兩大類:運行時異常和非運行時異常,開發(fā)人員在代碼中應當盡可能去處理這些異常,從而保證程序正確執(zhí)行完畢。
下表為Exception的構造方法和常用方法:

運行時的異常和非運行時的異常
各種具體的運行時異常都是RuntimeException類或者其他子類的對象,例如ClassCase-Exception(強制類型轉換異常),IndexOutBoundsException(下標越界類)等,因為這類異常只有在程序運行階段才能體現出來,所以Java編譯器在編譯階段對代碼是否處理了該類型異常不做檢查,編譯能夠正確通過,該類異常一般是由程序邏輯錯誤引起的,因此我們在編寫代碼的過程中應盡可能地避免這類型的錯誤。
常見的運行時異常:
ArithmeticException-------->算數除法運算中除數為0
舉例:
public class a {
public static void main(String[]args){
int a=10,b=0;
System.out.println(a/b);
}
}

ArrayIndexOutOfBoundsException----------->數組下標超界
舉例:
public class a {
public static void main(String[]args){
int a[]=new int[10];
System.out.println(a[10]);
}
}

NumberFormatException---------->數據格式化引發(fā)的異常
public class a {
public static void main(String[]args){
int i=Integer.parseInt("abc");
System.out.println(i);
}
}

ClassCastException---------->對象類型轉換不兼容
舉例:
public class a {
public static void main(String[]args){
a a=new a();
B b=(B)a;
}
}

NullPointerException----------->空引用引發(fā)的異常
public class a {
public static void main(String[]args){
a a=new a();
a=null;
a.getname();
}
public void getname(){
System.out.println("我是小芳");
}
}

常見的非運行異常:
SQLException:操作數據庫時查詢表可能發(fā)生的異常
IOException:操作文件時發(fā)生的異常
FileNotFoundException:操作不存在文件時發(fā)生的異常
ClassNotFoundException:加載類而類不存在時發(fā)生的異常
EOFException:操作文件到文件末尾發(fā)生異常
IllegalArguementException:參數異常
Java異常處理:
異常處理是指當異常發(fā)生后,程序能夠轉向相關的異常處理代碼中并執(zhí)行嘗試性修復處理,再根據修復處理的結果決定程序的走向,使應用程序能夠正常運行,或降級運行或安全地終止應用程序的執(zhí)行,以提高應用系統的可靠性。
try/catch/finally執(zhí)行情況:

try代碼段:
包含在try中的代碼段可能有多條語句會產生異常,但程序的一次執(zhí)行過程中如果產生異常,只可能是這些異常中的某一個,該異常對象由Java運行時系統生成并拋出,try中產生異常語句,之后的語句都不會被執(zhí)行,如果這次執(zhí)行過程中沒有產生異常,那么try中所有的語句都會被執(zhí)行。
catch代碼段:
捕獲try中拋出的異常并在其代碼段中做相應的處理,catch語句帶一個Throwable類型的參數,表示可能捕獲異常的類型。一般情況下,catch代碼段的數量由try中所拋出的異常個數決定,當try中代碼產生的異常被拋出后,catch代碼段按照從上到下的書寫順序將異常類型與自己參數所指向的異常類型進行匹配,若匹配成功程序轉而表示異常被捕獲,程序轉而執(zhí)行當前catch中的代碼,后面所有的catch代碼段都不會被執(zhí)行,如果匹配不成功,交給下一個catch進行匹配,如果所有catch都不匹配,表示當前方法不具備處理該異常的能力
對于這種情況如果是一個非運行時異常,為了編譯器通過,必須使用throws關鍵字聲明輸出。
finally代碼段:
該代碼段不是必須有的,但是如果有一定緊跟在最后一個catch代碼段后面,作為異常處理機制的統一出口(做善后處理).
無論try中是否產生異常,finally中的代碼總在當前方法返回之前無條件執(zhí)行。
注意:如果在某個catch代碼段中已經執(zhí)行了要終止程序的System.exit()方法,那么此時finally中的代碼不會執(zhí)行。
throw關鍵字:
用來在方法體內部創(chuàng)建異常對象并將其拋出,如果是非運行時異常,還必須結合throws關鍵字在方法頭部聲明拋出該異常,表明當前方法沒有處理該異常,將異常的處理任務延遲到當前方法的調用者,當前方法的調用者就必須檢查,處理,或者繼續(xù)拋出被調用方法拋出的異常
如果所有方法都層層上拋獲取的異常,最終會在main方法中尋找對應的catch代碼段。如果main方法中也沒有對異常進行捕獲,那么JVM將通過控制臺打印該異常消息和堆棧信息,同時程序也會終止。
throws關鍵字:
用來在方法頭部聲明方法可能會拋出的某些異常,僅當拋出了非運行時異常,該方法的調用者才必須處理或者重新拋出該異常。
如果方法的調用者無法處理該異常,應該繼續(xù)拋出而不是再catch中向控制臺打印異常發(fā)生時的堆棧信息,原因是堆棧信息對于用戶來說沒什么實在的意義。
try {
可能出現異常的程序代碼
}catch(異常類型1 異常對象名1){
異常類型1對應的異常處理代碼
} catch(異常類型2 異常對象名2){異常類型2對應的異常處理代碼
} finally{
無論是否發(fā)生異常,程序都必須執(zhí)行的代碼(善后代碼)
}
try,catch,finally關鍵字的使用:
這三個關鍵字既可以同時出現在程序中,也可以兩兩組合出現。
try+catch:
try{
//可能拋出異常的代碼
}
catch(異常類型 異常對象名){
//針對異常的處理代碼
}
舉例:
public class a {
public static void main(String[]args) {
int []arr=new int[5];
int i;
//數組下標為0-4的元素都根據默認值正常輸出了,但下標為5的元素不存在,所以便產生了數組下標越界的異常
try{
for( i=0;i<=arr.length;i++)
System.out.println(arr[i]);
}
//隨后catch捕獲到try中產生的異常,catch代碼段調用異常對象的`printStackTrace()方法輸出異常發(fā)生時堆棧中的信息
catch(ArrayIndexOutOfBoundsException e){
e.printStackTrace();
}
System.out.println("程序結束!");
}
}
輸出:
0
0
0
0
0
程序結束!
java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
at Employee.a.main(a.java:9)
通過打印出的堆棧信息,java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5at Employee.a.main(a.java:9),我們可以看出具體錯誤的信息,catch代碼段執(zhí)行完畢后,繼續(xù)執(zhí)行未執(zhí)行的代碼。
注意:catch代碼段用于處理異常,如果沒有catch代碼段就代表異常沒有被處理,如果該異常是非運行異常,那么必須聲明輸出,否則編譯不通過
try+finally:
try{
//可能拋出異常的代碼
}
finally{
無論異常是否發(fā)生,都無條件執(zhí)行的代碼
}
舉例:
還是上述實例,我們此時將catch換成了finally:
public class a {
public static void main(String[]args) {
int []arr=new int[5];
int i;
try{
for( i=0;i<=arr.length;i++)
System.out.println(arr[i]);
}
finally{
System.out.println("我是finally代碼段");
}
System.out.println("程序結束!");
}
}
輸出:
0
0
0
0
0
我是finally代碼段
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
at Employee.a.main(a.java:9)
對比兩種組合輸出的結果,看起來好像是一樣的,但是仔細看,會發(fā)現try+finally這種組合,并沒有執(zhí)行finally下面的語句,也就是沒有“程序結束”該語句的輸出,原因即為try中發(fā)生了異常,但是該程序不存在catch代碼段去捕獲異常,所以發(fā)生異常后面的語句都不會被執(zhí)行。
那么有的人會說finally中的語句為什么可以輸出呢?
有這個疑問的小伙伴上去再看一下finally的用法。
try+catch+finally:
try{
//可能拋出異常的代碼
}
catch(異常類型 異常對象名){
//針對異常的處理代碼
}finally{
無論異常是否發(fā)生,都無條件執(zhí)行的代碼
}
舉例:
public class a {
public static void main(String[]args) {
int []arr=new int[5];
int i;
try{
for( i=0;i<=arr.length;i++)
System.out.println(arr[i]);
}
catch(ArrayIndexOutOfBoundsException e){
e.printStackTrace();
}
finally{
System.out.println("我是finally代碼段");
}
System.out.println("程序結束!");
}
}
輸出:
0
0
0
0
0
我是finally代碼段
程序結束!
java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
at Employee.a.main(a.java:9)
這種組合輸出,當發(fā)生異常由catch捕獲處理完異常之后,繼續(xù)執(zhí)行下面未執(zhí)行完的語句。
注意?。。?/strong>
1:finally代碼段與其上面的catch代碼段之間不能再添加其他代碼語句。
2:try,finally這種形式由于沒有對異常進行任何的處理,所以一般不會應用在實際開發(fā)中。
trows和throw關鍵字的使用:
throws關鍵字的使用:
如果當前方法不對異常進行處理,可以通過聲明拋出異常,將處理該異常的任務交給當前方法的調用者,throws用在方法聲明部分的結尾處,表示該方法拋出異常,一個方法可以聲明拋出多個異常,這取決于方法中可能產生的異常個數,如果拋出多個異常,那么這些多個異常之間用逗號隔開,一個聲明了拋出異常的方法定義格式如下:
[修飾符] 返回值類型 方法名([參數列表])[throws 異常列表]{
// 方法體;
}
舉例:
public class a {
public static void print() throws ArrayIndexOutOfBoundsException{
int []arr=new int[5];
int i;
//print()方法體并沒有使用try-catch對ArrayIndexOutOfBoundsException異常進行處理,而是通過throws關鍵字聲明拋出
for( i=0;i<=arr.length;i++)
System.out.println(arr[i]);
}
public static void main(String[]args)throws ArrayIndexOutOfBoundsException{
//main()方法中對print()方法進行了調用,通過try-catch方式對print()方法拋出的異常進行捕獲處理。
try{
print();
}
catch(ArrayIndexOutOfBoundsException e){
e.printStackTrace();
}
}
}
輸出:
0
0
0
0
0
java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
at Employee.a.print(a.java:9)
at Employee.a.main(a.java:13)
當然main方法也可以不用try-catch方式處理,而是繼續(xù)通過trows關鍵字拋出。
那么運行結果通過運行結果,我們能夠了解什么信息呢?
首先通過輸出信息的最上部(異常入棧時位于棧底)入手去源文件中查找異常產生的原因。那么在本例中,我們通at Employee.a.print(a.java:9),應該去第九行尋找原因!
注意?。。?/strong>
如果一個方法通過trows聲明拋出了異常,那么調用該方法的其他方法可以通過try-catch方式進行捕獲處理,也可以繼續(xù)通過throws聲明將異常拋出。
一般不建議在main方法中通過trows聲明拋出異常原因為:Java中發(fā)生異常如果一直上拋,最終拋給了main方法,main方法繼續(xù)上拋,拋給了調用者JVM,JVM終止程序的執(zhí)行。
這樣看來好像也沒什么錯,但是異常處理機制的作用就是提高程序的健壯性,保證程序出現了異常也能執(zhí)行,所以main方法中的異常建議是使用try-catch進行捕捉,而不是繼續(xù)上拋!
trow關鍵字的使用:
throw關鍵字主要用在方法體中對異常進行拋出,通常方法體中捕獲到相關異常對象后并不進行處理,將對象的處理交給當前方法的調用者,一個通過trow關鍵字聲明拋出異常的方法定義格式如下:
[修飾符] 返回值類型 方法名([參數列表])[throws 異常列表]{
// 方法體;
trow異常對象;
}
舉例:
public class a {
public static void print(){
int []arr=new int[5];
int i;
try{
for( i=0;i<=arr.length;i++)
System.out.println(arr[i]);
}
//該catch并沒有對異常進行處理,而是通過trow將異常對象拋出了
catch(ArrayIndexOutOfBoundsException e){
throw e;
}
}
public static void main(String[]args){
try{
print();
}
//main方法中的catch捕獲到try中調用print方法的異常并對其進行處理
catch(ArrayIndexOutOfBoundsException e){
e.printStackTrace();
}
}
}
輸出:
0
0
0
0
0
java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
at Employee.a.print(a.java:8)
at Employee.a.main(a.java:16)
提醒:
由于這里的ArrayIndexOutOfBoundsException是運行時異常,所以print()方法聲明部分的結尾不再需要throws,如果throw拋出的是某個非運行異常,那么throw所在的方法的聲明尾部還需要通過throws聲明將這個非運行時異常對象拋出。
自定義異常:
在實際開發(fā)中,異常正所謂是“千姿百態(tài)”,但JDK提供給我們的異常類型是很有限的,因此面對實際開發(fā)中的各種異常問題,我們除了靈活使用JDK提供給我們的之外,還需要我們自定義一些異常,這些異常也就是我們在面向對象編程的過程中,出現的特有問題。
既然是自定義,那么由于每個人想法和處理問題的方式不同,很容易出現操作異常等問題,為了解決這種問題,我們規(guī)定自定義異常必須繼承Exception或者RuntimeException,從Exception繼承表示自定義異常是非運行時異常,從RuntimeException繼承表示自定義異常是運行時異常,當腰操作自定義異常的信息時,可以使用父類已經定義好的方法
自定義異常的一般形式如下:
class 異常類名 extends Exception|RunException
{
//類體
}
舉例:
當輸入的分數小于0時,捕獲異常并輸出分數不能小于0的提示信息。
主類:
package exception;
import java.util.Scanner;
public class exception {
public static void show_score() throws Text {
//該方法并沒有對分數小于0的這種情況進行任何的處理,而是創(chuàng)建異常對象將它拋出
int score;
Scanner scanner=new Scanner(System.in);
score=scanner.nextInt();
if(score<0){
throw new Text("分數不能小于0");
}
}
public static void main(String[]args){
//在main方法中捕獲到該異常,并對其進行處理
try{
show_score();
} catch (Text e) {
e.printStackTrace();
}
}
}
由于JDK并沒有為我們提供某一個異常來描述分數不能小于0的這種情況,所以需要自定義異常類[這里的異常類為Text類]來表示分數不能小于0的這種情況,且該異常是非運行異常,因此需要繼承Exception類。
自定義異常類:
package exception;
public class Text extends Exception{
public Text(String message) {
super(message);
}
}
輸出:

注意:如果該異常屬于RuntimeException,那么在方法的尾部不需要throws聲明將該異常拋出,反之如果是Exception,則需要聲明拋出
異常處理事項:
如果一個方法產生的異常不止一種,且這些異常具有父子關系,那么書寫catch代碼塊時,處理異常的catch塊要位于處理子異常catch塊的后面。
依然是上述事例:
public static void main(String[]args){
try{
show_score();
}
catch (Text e) {
e.printStackTrace();
}
catch(Exception e){
e.printStackTrace();
}
由于Text異常類繼承了Exception類,所以處理父類Exception類的catch代碼塊必須寫在子類Text的后面。
在進行方法覆蓋時,如果被覆蓋的方法拋出異常,那么覆蓋方法可以不拋異常,或者拋與被覆蓋方法相同的異常,或者拋被覆蓋方法的所拋異常的子異常。
定義父類拋出Exception類異常:
package exception;
public class Father {
public void show() throws Exception{
int a=10;
if(a<100){
throw new Exception();
}
System.out.println("hello,Java");
}
}
子類對象對父類中的show方法進行覆蓋:
package exception;
public class Son extends Father{
@Override
public void show() throws Exception {
System.out.println("helloJava");//拋出和父類相同的異常
}
@Override
public void show() throws Text{//拋出父類拋出的異常的子異常類
System.out.println("helloJava");
}
@Override
public void show() {//未拋出異常
System.out.println("helloJava");
}
}
以上三種方式均正確!
如果try代碼中有return語句返回基本數據類型變量,即使finally中對該基本數據類型變量進行修改,返回結果以try中修改的值為準。
舉例:
package exception;
import java.util.Scanner;
public class exception {
private static int getnumber(){
int i=0;
try{
i=100;
return i;
}catch (Exception e){
e.printStackTrace();
}finally//在finally中改變基本數據類型變量i的值
i=1000;
}
return i;
}
public static void main(String[]args){
System.out.println(getnumber());
}
}
輸出:
100//try中的值
如果try代碼中有return語句,返回引用數據類型變量,finally中對該引用數據類型變量進行修改,返回結果以finally中修改的值為準。
舉例:
package exception;
import java.util.Scanner;
public class exception {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private static exception getname(){
exception exception=new exception();
try{
exception.setName("張三");
return exception;
}catch (Exception e){
e.printStackTrace();
}finally {
exception.setName("小張");
}return exception;
}
public static void main(String[]args){
System.out.println(getname().getName());
}
}
輸出:
李四//finally中的值
如果try,finally代碼中都有return語句,無論返回什么數據類型,返回結果以finally中修改的值為準。
舉例:
package exception;
import java.util.Scanner;
public class exception {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private static exception getname(){
//try和finally語句中都含有return語句
exception exception=new exception();
try{
exception.setName("張三");
return exception;
}catch (Exception e){
e.printStackTrace();
}finally {
exception.setName("小張");
return exception;
}
}
public static void main(String[]args){
System.out.println(getname().getName());
}
}
輸出:
小張//finally中的值
總結
到此這篇關于Java異常處理機制知識整理的文章就介紹到這了,更多相關Java異常處理機制內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
使用Spring-Retry解決Spring Boot應用程序中的重試問題
重試的使用場景比較多,比如調用遠程服務時,由于網絡或者服務端響應慢導致調用超時,此時可以多重試幾次。用定時任務也可以實現重試的效果,但比較麻煩,用Spring Retry的話一個注解搞定所有,感興趣的可以了解一下2023-04-04
Idea配置maven-tomcat-plugin插件實現項目部署
今天小編就為大家分享一篇關于Idea配置maven-tomcat-plugin插件實現項目部署,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-02-02
java線程池對象ThreadPoolExecutor的深入講解
在我們的開發(fā)中“池”的概念并不罕見,有數據庫連接池、線程池、對象池、常量池等等。下面這篇文章主要給大家介紹了關于java線程池對象ThreadPoolExecutor的相關資料,需要的朋友可以參考借鑒,下面來一起看看吧2018-09-09
Java if(boolean)和if(boolean=true)區(qū)別解析
這篇文章主要介紹了Java if(boolean)和if(boolean=true)區(qū)別解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-02-02

