java異常處理機制

  1. java異常是Java提供的用於處理程序中錯誤的一種機制。
  2. 所謂錯誤是指程序在運行過程當中發生的一些異常事件(如除數爲0、數組下標越界、操做的文件不存在等)。
  3. Java程序在執行過程當中若是出現異常事件,能夠生成一個異常類對象,該異常對象封裝了異常事件的信息並將被提交給Java運行時系統,這個過程稱爲拋出(throw)異常。
  4. 當Java運行時系統收到異常對象時,會尋找能處理這一異常的代碼並將對當前異常對象交給其處理,這一過程稱爲異常捕獲(catch)。

JAVA中的異常類都繼承自Throwable類,也就是說,這是異常類的根。Throwable類擴展了兩個類Error類和Exception類,Exception類又擴展了一個RuntimeException類。以下圖:javascript

  • Error:稱爲錯誤,由Java虛擬機生成並拋出,這類錯誤通常是運行時系統內部的錯誤,沒法被處理。
  • Exception:全部異常類的父類,其子類對應了各類各樣可能出現的異常事件,通常須要用戶顯式地聲明或捕獲。如文件類異常:FileNotFoundException,IOExecption。
  • RuntimeException:一種特殊的異常類,繼承自Exception類。如除數爲0、數組下標越界等。這類異常產生比較頻繁,用戶通常不對其作捕獲處理,不然對程序的可讀性和運行效率影響很大,而是由系統自動檢測並將它們交給默認的異常處理程序進行處理。如ArithmeticException,ArrayIndexOutOfBoundException。

通常來講,出現RuntimeException異常表示的是代碼不合理而出現的問題。php

  • 未檢查異常:Error錯誤和RuntimeException類的異常;
  • 已檢查異常:Exception類的異常但不包括RuntimeException類。

所以,在自定義異常類型時,大多數都直接繼承Exception類,偶爾可能繼承RuntimeException類,更偶爾的可能會繼承這些類的某些子類。css

try-catch-finally結構和處理流程

使用try-catch結構捕捉異常,並設置捕捉到後的處理方式。還能夠加上finally結構,這是可選結構,但卻表示try結構中必須會被執行的部分。html

如下是try-catch-finally結構和處理過程的分析。java

try {
  // 待捕捉測試的代碼1
  // 待捕捉測試的代碼2 (假設此爲異常代碼,將拋出名爲異常名2的異常)
  // 待捕捉測試的代碼3
} catch (異常名1 對象名e) {
  // 捕捉到異常名1的異常後,該作的處理代碼4
} catch (異常名2 對象名e) {
  // 捕捉到異常名2的異常後,該作的處理代碼5
} ... {
  //...
} finally {
  //必定會執行的代碼6
}

  //try結構外的代碼7

前提假設,在各代碼中沒有return子句。執行過程爲:首先代碼1正常執行,到代碼2處拋出異常名2的異常,經過異常名匹配,將選擇第二個catch結構,因而將異常2封裝到對象名e中,並執行代碼5處理異常。catch部分處理完後,還有最後處理段finally,因而執行代碼6。出了finally後,還將執行代碼7。nginx

注意,當代碼2出現異常後,代碼3不會執行。而finally則是不管是否真的捕捉到了異常、是否在catch段有return都會執行的代碼段。換句話說,finally段的代碼除了內部錯誤或外界影響都必定會執行就像下面的例子中,即便catch使用了return,但finally仍是會執行,只不過這個catch終止了try結構外的代碼。git

例如,除數爲0時會拋出ArithmeticException異常。try-catch捕捉它:github

public class TEx {
    public static void main(String[] args) {
        try {
            System.out.println("[start]");
            System.out.println(2/0);
            System.out.println("[end]");
        } catch (ArithmeticException e) {
            System.out.println("[Catching]: " + e);
            return;
        } finally {
            System.out.println("[Finally]");
        }
        System.out.println("[out of try-catch]");
    }
}

在finally段中還能夠繼續try-catch-finally,防止該段落的代碼再次拋出異常。web

public class TEx {
    public static void main(String[] args) {
        try {
            System.out.println("[start]");
            System.out.println(2/0);
            System.out.println("[end]");
        } catch (ArithmeticException e) {
            System.out.println("[Catching]: " + e);
            return;
        } finally {
            try {
                System.out.println("[Finally-try-start]");
                System.out.println(3/0);
            } catch (ArithmeticException e) {
                System.out.println("[Finally-Catching]: " + e);
            }
        }
        System.out.println("[out of try-catch]");
    }
}

輸出異常信息

java中的異常都會封裝到對象中。異常對象中有幾個方法:django

  • printStackTrace():輸出最詳細的信息,包括拋出異常的行號,異常信息以及異常緣由。
  • getMessage():輸出異常信息。
  • getCause():輸出異常緣由。

異常拋出過程和throw、throws關鍵字

throw關鍵字用於在某個語句處拋出一個異常,只要執行到這個語句就表示一定拋出異常。
throws關鍵字用於在方法處拋出一個或多個異常,這表示執行這個方法可能會拋出異常。

throw OBJECT;
throw new EXCEPTION("Message");
method() throws EXCEPTION1[,EXCEPTION2...] {}

對於Exception類(非RuntimeException)的異常即已檢查異常類,在調用時要麼進行捕捉,要麼繼續向上級拋出。這類錯誤產生和處理的過程爲:

  1. 方法f()內部的方法體的throw向上拋出給方法f();
  2. 方法f()的throws向上拋出拋給f()調用者;
  3. 方法調用者必須捕捉處理,或者不想捕捉就繼續向上拋出;
  4. 每一級的調用者都不想捕捉而是一直向上拋出,則最後由java虛擬機報錯:"未報告的異常錯誤XXXX必須對其進行捕獲或聲明以便拋出"。

如下是拋出異常的一個簡單示例,拋出的是ArithmeticException異常,由於是RuntimeException類異常,所以從方法體內部throw拋出後,無需在方法定義處使用throws繼續拋出。

public class EX {
    void f(int n) {          // 或void f(int n) throws ArithmeticException {}
        if (n == 0) {
            throw new ArithmeticException("hello Exception!");
        } else {
            System.out.println("right!");
        }
    }

    public static void main(String[] args) {
            EX m = new EX();
            m.f(1);
            m.f(0);   // throw Exception
    }
}

執行結果:

right!
Exception in thread "main" java.lang.ArithmeticException: hello Exception!   //異常的信息
        at EX.f(EX.java:4)      //真實產生異常的地方
        at EX.main(EX.java:13)  //調用產生異常的地方

因此,對於RuntimeException類異常來講,是否使用throws關鍵字並沒有影響。通常來講,Exception類異常但非RuntimeException才須要使用throws關鍵字,由於Exception類異常必需要被捕獲並處理,而RuntimeException異常則無所謂。

例如將上面的ArimeticException改成FileNotFoundException,前者爲Runtime類異常,然後者爲Exception但非Runtime類異常,所以使用throw拋出後,必須在定義方法處也使用throws拋出錯誤。這一過程是"向上級拋出"的過程:"方法體內部拋出異常-->拋給方法自己"

void f(int n) throws FileNotFoundException {
    if (n == 0) {
        throw new FileNotFoundException("hello Exception!");  //throw a new yichang duixiang
    } else {
        System.out.println("right!");
    }
}

若是不使用throws關鍵字拋出錯誤,則將報錯:

EX.java:6: 錯誤: 未報告的異常錯誤FileNotFoundException; 必須對其進行捕獲或聲明以便拋出
       throw new FileNotFoundException("hello Exception!");  //throw a new yichang duixiang

從方法f()拋出向上拋出給調用者後,調用者要麼使用try-catch捕捉,要麼繼續向上拋出,不然報錯。例如捕捉

import java.io.*;

public class EX {
    void f(int n) throws FileNotFoundException {
        if (n == 0) {
            throw new FileNotFoundException("hello Exception!");
        } else {
            System.out.println("right!");
        }
    }

    public static void main(String[] args) {
      try {
          EX m = new EX();
          m.f(0);
      } catch (FileNotFoundException e) {
         System.out.println(e); 
      }
        System.out.println("out of try-catch");
    }
}

若是不捕捉,則能夠繼續在方法處向上拋出:

public static void main(String[] args) throws FileNotFoundException {
          EX m = new EX();
          m.f(0);
    }

拋出異常時的注意事項

throw能夠同時定義可能拋出的多種異常,儘管這些異常存在繼承關係。這時在捕捉異常時,應該先捕捉子類,再捕捉父類。

例如FileNotFoundException是IOException的子類,能夠同時:

throws FileNotFoundException,IOException

捕捉時應先捕捉FileNotFoundException,再IOException。

try {
...
} catch (FileNotFoundException e) {
...
} catch (IOException e) {
...
}

在重寫有throws子句的方法時,須要注意:

  1. 子類重寫父類方法要拋出與父類一致的異常,或者不拋出異常
  2. 子類重寫父類方法所拋出的Exception類異常不能超過父類的範疇
  3. 子類重寫父類方法拋出的異常能夠超出父類範疇,但超出的部分必須是RuntimeException類的異常

因此下面的定義中,前子類1-3重寫和子類5-7都是有效的,但子類4重寫是錯誤的。

父類:method() throws IOException {} 子類1:method() throws {} 子類2:method() throws IOException {} 子類3:method() throws FileNotFoundException {} 子類4:method() throws Exception {} 子類5:method() throws RuntimeException {} 子類6:method() throws IOException,RuntimeException {} 子類7:method() throws IOException,ArithmeticException {}

自定義異常

異常是類,當產生異常時會構建此類的異常對象。

自定義異常時須要考慮異常類的構造方法是否接參數,參數須要如何處理實現怎樣的邏輯。

自定義的異常通常都從Exception繼承。

例以下面定義了一個銀行卡存、取錢時的程序,其中自定義了一個Exception類錯誤。

// User Define Exception
class OverDrawException extends Exception {
    private double amount;
    public OverDrawException(double amount) {
        this.amount = amount;
    }

    public OverDrawException(String message) {
        super(message);
    }

    public double getAmount() {
        return amount;
    }
}

// Card class
class Card {
    //cardNum:卡號,balance:餘額
    private String cardNum;
    private double balance;

    Card(String n,double b) {
        this.cardNum = n;
        this.balance = b;
    }

    //方法:存錢
    public void cunQian(double n) {
        balance += n;
    }

    //方法:取錢
    public void quQian(double n) throws OverDrawException {
        if (n <= this.balance) {
            balance -= n;
        } else {
            double need = n - balance;
            throw new OverDrawException(need);
        }
    }

    //方法:返回餘額
    public double getBalance() {
        return this.balance;
    }
}

public class SuanZhang {
    public static void main(String [] args) {
        try {
            Card card = new Card("62202",300);
            System.out.println("卡里餘額:" + card.getBalance());
            //存錢
            card.cunQian(200);
            System.out.println("餘額:" + card.getBalance());
            //取錢
            card.quQian(600);
            System.out.println("餘額:" + card.getBalance());
        } catch (OverDrawException e) {
            System.out.println(e);
            System.out.println("抱歉!您的餘額不足,缺乏:" + e.getAmount());
        }
    }
}

注:若您以爲這篇文章還不錯請點擊右下角推薦,您的支持能激發做者更大的寫做熱情,很是感謝!

相關文章
相關標籤/搜索