Java 基礎(十二)異常機制

異常

一說談到異常,可能就有小夥伴說,這個啊,我 try...catch...finally用得賊溜。別急哈,後面有個 案例,看完以後你確定會以爲本身對異常的理解也不是那麼透徹了,同時本文還可能會掃到你的一些知識盲點。額,扯遠了~程序員

異常通常指不期而至的各類情況,如:文件不存在、空指針、非法參數等。
異常是一個事件,發生在程序運行期間,干擾了正常的指令流程。
Java 中使用 Throwable 類及其子類來描述各類不一樣的異常。所以,Java 異常都是對象,是 Throwable 的子類實例,描述了出如今一段編碼中的錯誤條件。當條件生成時,錯誤將引起異常。數據庫

Java 異常類層次結構圖:編程

從圖上咱們能夠看到,Java 異常都繼承自 Throwable,Throwable 分爲兩大派系,一類是 Error(錯誤),一類是 Exception(異常)。數組

Error

Error 是程序員沒法處理的錯誤,表示運行應用程序中教嚴重的問題。大多數錯誤與代碼編寫者執行的操做無關,而表示代碼運行時 JVM 出現的問題。好比,Java 虛擬機運行時錯誤(VirtualMachineError),當 JVM 再也不有繼續執行操做所需的內存資源時,將出現 OutOfMemoryError。這時異常發生時,Java 虛擬機通常會終止線程。安全

因此這一類異常咱們通常不用太糾結。bash

Exception

這是程序自己能夠處理的異常。
Exception 有一個很是重要的子類 RuntimeException,RuntimeException 類及其子類表示 JVM 經常使用操做引起的錯誤,例如空值對象引用、除數爲零、數組角標越界則分別會引起 NullPointException、ArithmeticException、ArrayIndexOutOfBoundException。學習

敲黑板!!分清異常和錯誤的區別,異常是能夠被程序自己處理的,錯誤不能。ui

可查異常

又稱編譯時異常,是編譯器要求必須處置的異常。
正確的程序在運行中,很容易出現的、情理可容的異常情況。可查異常雖然是異常情況,但在必定程度上它的發生是能夠預計的,並且一旦發生這種異常情況,就必須採起某種方式進行處理。編碼

除了RuntimeException及其子類之外,其餘的Exception類及其子類都屬於可查異常。這種異常的特色是Java編譯器會檢查它,也就是說,當程序中可能出現這類異常,要麼用try-catch語句捕獲它,要麼用throws子句聲明拋出它,不然編譯不會經過。spa

不可查異常

編譯器不要求強制處置的異常,包括運行時異常(RuntimeException)和錯誤(Error)。

運行時異常

都是 RuntimeException 類及其子類。

這些異常是不檢查異常,程序中能夠選擇捕獲處理,也能夠不處理。這些異常通常是由程序邏輯錯誤引發的,程序應該從邏輯角度儘量避免這類異常的發生。

編譯時異常

是RuntimeException之外的異常,類型上都屬於Exception類及其子類。從程序語法角度講是必須進行處理的異常,若是不處理,程序就不能編譯經過。

處理異常機制

遇到異常怎麼辦?要麼本身想辦法處理,要麼就拋給別人。

拋出異常:
當一個方法出現錯誤引起異常時,方法建立異常對象並交付給運行時系統,系統對象中包含了異常類型和異常出現時的程序狀態等異常信息。運行時系統負責尋找處置異常的代碼並執行。

捕獲異常:
在方法拋出異常以後,運行時系統將轉爲尋找合適的異常處理器(Exception handler)。潛在的異常處理是異常發生時依次存留在調用棧中的方法的集合。當異常處理器所能處理的異常類型與拋出的異常類型相符時,即爲合適的異常處理器。運行時系統從發生異常的方法開始,依次回查調用棧中的方法,直至找到含有異常處理器的方法並執行。當運行時系統遍歷了調用棧都沒找到合適的異常處理器,則運行時系統終止,Java 程序終止。

對於運行時異常、錯誤和可查異常,Java 要求的異常處理方式有所不一樣。

  • 對於方法運行中可能出現的 Error,當運行方法不能捕捉時,Java 容許該方法不作任何拋出聲明。由於大多數 Error 異常屬於永遠不容許發生的狀況,也屬於合理的應用程序不掛捕獲的異常。
  • 對於全部可查異常,Java 規定:一個方法必須捕捉或者聲明拋出。

可以捕捉異常的方法,須要提供相符類型的異常處理器。也就是說,一個方法所能捕獲的異常,一點是 Java 代碼在某處所拋出的異常

任何Java代碼均可以拋出異常,如:本身編寫的代碼、來自Java開發環境包中代碼,或者Java運行時系統。不管是誰,均可以經過Java的throw語句拋出異常。

從方法中拋出的任何異常都必須使用throws子句。

捕捉異常經過try-catch語句或者try-catch-finally語句實現。

總結來書。Java 規定:對於可查異常必須捕捉、或者聲明拋出。運行忽略不可查的 RuntimeException 和 Error。

捕獲異常 try、catch 和 finally

  • 首先是 try-catch 語句

基本語法以下:

try {  
    // 可能會發生異常的程序代碼  
} catch (Type1 id1){  
    // 捕獲並處置try拋出的異常類型Type1  
} catch (Type2 id2){  
     //捕獲並處置try拋出的異常類型Type2  
}複製代碼

關鍵詞 try 後面的大括號區域爲可能發生異常的代碼,稱爲監控區。當拋出異常或者出現運行時異常,而後由 Java 運行時系統巡展匹配 catch 子句。如有匹配的catch子句,則運行其異常處理代碼,try-catch語句結束。

匹配規則:拋出的異常屬於 catch 語句捕獲異常類及子類,則匹配成功。

須要注意的是:一旦某個catch捕獲到匹配的異常類型,將進入異常處理代碼。一經處理結束,就意味着整個try-catch語句結束。其餘的catch子句再也不有匹配和捕獲異常類型的機會,而且 try 裏面若是有沒有走完的語句也會自動跳過

  • try-catch-finally

這個很簡單,不管 try-catch 是正常結束仍是發生異常捕獲,都會執行 finally 語句,若是在 finally 以前發生了 return,那麼 finally 語句塊會在 retrun 以前執行。

小結:

try 塊:用於捕獲異常。其後可接零個或多個catch塊,若是沒有catch塊,則必須跟一個finally塊。
catch 塊:用於處理try捕獲到的異常。
finally 塊:不管是否捕獲或處理異常,finally塊裏的語句都會被執行。當在try塊或catch塊中遇到return語句時,finally語句塊將在方法返回以前被執行。
finally 若是有 return 會覆蓋 catch 裏的throw,一樣若是 finally 裏有 throw 會覆蓋 catch 裏的 return。

try-catch-finally 規則

1.必須在 try 後面添加 catch 或 finally。能夠同時存在,catch 塊也能夠有多個,可是 catch 必須放在 try 塊或者 catch 塊後面。
2.try-catch-finally 能夠嵌套
3.在 try-catch-finally 結構中,能夠從新拋出異常
4.除非 finally 中拋出異常或者 JVM 中止運行,不然都會執行 finally 語句

try-catch-finally 語句執行順序
1.當 try 沒有捕獲到異常時:try 語句正常執行,程序跳過 catch 語句,執行 finally。

2.當 try 捕獲到異常時,catch 沒有匹配上異常,則交個 JVM 處理,finally 語句塊仍是會被執行,可是 finally 以後的語句不會被執行了。

3.當 try 捕獲到異常,catch 匹配上了,則按照 try-catch-finally 的正常順序執行,可是 try 裏面異常語句以後的語句不會被執行。

拋出異常

任何 Java 代碼均可以拋出異常,語句經過 throw 拋出、方法則是 throws。

爲何要 throw 異常?

若是一個方法可能會出現異常,可是沒有能力處理這種又一次,則能夠在方法聲明處用 throws 語句來拋出。

throws 語句能夠同時聲明拋出多個異常,語法格式以下

void methodName()throws Exception1,Exception2,Exception3{
}複製代碼

就醬紫。此時這個方法就不用對方法重點問這些異常作任何處理,可是調用這個方法的語句必需要處理,要麼繼續拋,要麼 try-catch。

Throws 拋出規則

1.若是是不可查異常,那麼不用聲明,編譯仍然能夠經過,可是運行時會被JVM 拋出
2.若是是可查異常,要麼 try-catch,要麼 throws,不然編譯沒法經過。
3.當拋出了異常時,該方法的調用者纔會處理或者從新拋出該異常。
4.調用 throws 異常的方法,必須 try-catch 調用語氣,且 catch 的異常必須是拋出異常的同類或者父類。

Throw 拋出異常
這個真沒什麼好說的了,想拋或者須要拋,就直接調用就好了,注意 throw 後面只能接 Throwable 的子類。

Throwable 類經常使用方法

這個本應該在開篇就講的,可是好像都不怎麼經常使用,因此就放在這裏查漏補缺吧

返回值 方法名 方法說明
Throwable fillInStackTrace() 在異常堆棧跟蹤中填充
Throwable getCause 返回 Throwable 的 cause
String getLocalizedMessage 建立此 Throwable 的本地化描述
String getMessage 返回此 Throwable 的詳細消息字符串
StackTraceElement[] getStackTrace() 提供編程訪問由printStackTrace 輸出的堆棧跟蹤信息
Throwable initCause 將此 throwable 的 cause 初始化爲指定值
void printStackTrace 將此 Throwable 及其追蹤輸出至標準錯物流
String toString 返回此 Throwable 的簡短描述

Java 常見的異常

  • 運行時異常
    1.ArrayIndexOutOfBoundException
    數組索引越界異常。
    2.ArithmeticException
    算術條件異常,如除數爲0
    3.NullPointException
    空指針異常。
    4.ClassNotFoundException
    找不到類異常。
    5.NegativeArraySizeException
    數組長度爲負異常
    6.ArrayStoreException
    數組中包含不兼容的值拋出異常
    7.SecurityException
    安全性異常
    8.IllegalArgumentException
    非法參數異常

  • IOException
    1.IOException
    操做輸入流和輸出流可能出現的異常
    2.EOFException
    文件已結束異常
    3.FileNotFoundException
    文件未找到異常

  • 其餘
    1.ClassCastException
    類型轉換異常類
    2.ArrayStoreException
    數組中包含不兼容的值拋出的異常
    3.SQLException
    操做數據庫異常類
    4.NoSuchFieldException
    字段未找到異常
    5.NoSuchMethodException
    方法未找到拋出的異常
    6.NumberFormatException
    字符串轉換爲數字拋出的異常
    7.StringIndexOutOfBoundsException
    字符串索引超出範圍拋出的異常
    8.IllegalAccessException
    不容許訪問某類異常
    9.InstantiationException
    當應用程序試圖使用Class類中的newInstance()方法建立一個類的實例,而指定的類對象沒法被實例化時,拋出該異常

自定義異常

使用Java 內置的異常類能夠描述在編程中出現的大部分狀況,除此以外,用戶還能夠自定義異常。自定義異常類,只須要繼承 Exception 便可。

在程序中使用自定義異常類,大致可分爲如下幾個步驟。

1.建立自定義異常類。
2.在方法中經過throw關鍵字拋出異常對象。
3.若是在當前拋出異常的方法中處理異常,可使用try-catch語句捕獲並處理;不然在方法的聲明處經過throws關鍵字指明要拋出給方法調用者的異常,繼續進行下一步操做。
4.在出現異常方法的調用者中捕獲並處理異常。

練習

異常差很少就到這裏吧,作完這個小練習,若是能作對的話就差很少了,若是作不對,那再回過頭去從新讀一遍這篇文章。

//執行語句
TestException testException1 = new TestException();
    try {
        testException1.testEx();
    } catch (Exception e) {
        e.printStackTrace();
    }

//異常類
public class TestException {
    public TestException() {
    }

    boolean testEx() throws Exception {
        boolean ret = true;
        try {
            ret = testEx1();
        } catch (Exception e) {
            Log.e("sss___sss","testEx, catch exception");
            ret = false;
            throw e;
        } finally {
            Log.e("sss___sss","testEx, finally; return value=" + ret);
            return ret;
        }
    }

    boolean testEx1() throws Exception {
        boolean ret = true;
        try {
            ret = testEx2();
            if (!ret) {
                return false;
            }
            Log.e("sss___sss","testEx1, at the end of try");
            return ret;
        } catch (Exception e) {
            Log.e("sss___sss","testEx1, catch exception");
            ret = false;
            throw e;
        } finally {
            Log.e("sss___sss","testEx1, finally; return value=" + ret);
            return ret;
        }
    }

    boolean testEx2() throws Exception {
        boolean ret = true;
        try {
            int b = 12;
            int c;
            for (int i = 2; i >= -2; i--) {
                c = b / i;
                Log.e("sss___sss","i=" + i);
            }
            return true;
        } catch (Exception e) {
            Log.e("sss___sss","testEx2, catch exception");
            ret = false;
            throw e;
        } finally {
            Log.e("sss___sss","testEx2, finally; return value=" + ret);
            return ret;
        }
    }
}複製代碼

求以上語句的答應結果~

本身先讀一遍,再來對答案哦~

sss___sss: i=2
sss___sss: i=1
sss___sss: testEx2, catch exception
sss___sss: testEx2, finally; return value=false
sss___sss: testEx1, finally; return value=false
sss___sss: testEx, finally; return value=false複製代碼

好了,異常機制的學習就到這裏吧。

相關文章
相關標籤/搜索