1. 程式人生 > >Java----異常類(錯誤和異常,兩者區別)

Java----異常類(錯誤和異常,兩者區別)

簡述
程式執行時,發生了不被期望的結果,阻止了程式按照預期正常執行,這就是異常。世界上沒有不出錯的程式,只有正確處理好意外情況,才能保證程式的可靠性。

Java 語言在設計之初就提供了相對完善的異常處理機制,這也是 Java 得以大行其道的原因之一,因為這種機制大大降低了編寫和維護可靠程式的門檻。如今,異常處理機制已經成為現代程式語言的標配。

異常處理機制能讓程式在異常發生時,按照程式碼的預先設定的異常處理邏輯,針對性地處理異常,讓程式盡最大可能恢復正常並繼續執行,且保持程式碼的清晰。

Java中的異常可能是函式中的語句執行時引發的,也可能是程式設計師通過throw語句手動丟擲的,只要Java程式中產生了異常,就需要用一個對應型別的異常物件來封裝異常,JRE就會試圖尋找相應的異常處理程式來處理異常。

Throwable類是Java異常型別的頂層父類,一個物件只有是 Throwable 類的例項,才是一個異常物件,才能被異常處理機制識別。JDK中內建了一些常用的異常類,我們也可以自定義異常

Java異常分類
Java標準庫內建了一些通用的異常類,這些類以Throwable為頂層父類。

Throwable又派生出Error類和Exception類

錯誤: Error類以及它的子類的例項,代表了JVM本身的錯誤。錯誤不能被程式設計師通過程式碼處理,Error很少出現。因此,程式設計師應該關注Exception為父類的分支下的各種異常類。

異常: Exception以及它的子類,代表程式執行時傳送的各種不期望發生的事件。可以被Java異常處理機制使用,是異常處理的核心。

總體上我們根據Javac對異常的處理要求,將異常類分為2類。

非檢查性異常(unckecked exception):

Error 和 RuntimeException 以及子類。javac在編譯時,不會提示和發現這樣的異常,我們可以編寫程式碼處理(使用try…catch…finally)這樣的異常,也可以不處理。對於這些異常,我們應該修正程式碼,而不是去通過異常處理器處理 。這樣的異常發生的原因多半是程式碼寫的有問題。如除0錯誤ArithmeticException,錯誤的強制型別轉換錯誤ClassCastException,陣列索引越界ArrayIndexOutOfBoundsException,使用了空物件NullPointerException等等。

Java 根據各個類庫也定義了一些其他的異常,下面的表中列出了 Java 的非檢查性異常。

異常    描述
ArithmeticException    當出現異常的運算條件時,丟擲此異常。例如,一個整數"除以零"時,丟擲此類的一個例項。
ArrayIndexOutOfBoundsException    用非法索引訪問陣列時丟擲的異常。如果索引為負或大於等於陣列大小,則該索引為非法索引。
ArrayStoreException    試圖將錯誤型別的物件儲存到一個物件陣列時丟擲的異常。
ClassCastException    當試圖將物件強制轉換為不是例項的子類時,丟擲該異常。
IllegalArgumentException    丟擲的異常表明向方法傳遞了一個不合法或不正確的引數。
IllegalMonitorStateException    丟擲的異常表明某一執行緒已經試圖等待物件的監視器,或者試圖通知其他正在等待物件的監視器而本身沒有指定監視器的執行緒。
IllegalStateException    在非法或不適當的時間呼叫方法時產生的訊號。換句話說,即 Java 環境或 Java 應用程式沒有處於請求操作所要求的適當狀態下。
IllegalThreadStateException    執行緒沒有處於請求操作所要求的適當狀態時丟擲的異常。
IndexOutOfBoundsException    指示某排序索引(例如對陣列、字串或向量的排序)超出範圍時丟擲。
NegativeArraySizeException    如果應用程式試圖建立大小為負的陣列,則丟擲該異常。
NullPointerException    當應用程式試圖在需要物件的地方使用 null 時,丟擲該異常
NumberFormatException    當應用程式試圖將字串轉換成一種數值型別,但該字串不能轉換為適當格式時,丟擲該異常。
SecurityException    由安全管理器丟擲的異常,指示存在安全侵犯。
StringIndexOutOfBoundsException    此異常由 String 方法丟擲,指示索引或者為負,或者超出字串的大小。
UnsupportedOperationException    當不支援請求的操作時,丟擲該異常。
檢查性異常(checked exception):

除了Error 和 RuntimeException的其它異常。javac強制要求程式設計師為這樣的異常做預備處理工作(使用try…catch…finally或者throws)。在方法中要麼用try-catch語句捕獲它並處理,要麼用throws子句宣告丟擲它,否則編譯不會通過。這樣的異常一般是由程式的執行環境導致的。因為程式可能被執行在各種未知的環境下,而程式設計師無法干預使用者如何使用他編寫的程式,於是程式設計師就應該為這樣的異常時刻準備著。如SQLException , IOException,ClassNotFoundException 等。

下面的表中列出了 Java 定義在 java.lang 包中的檢查性異常類。

異常    描述
ClassNotFoundException    應用程式試圖載入類時,找不到相應的類,丟擲該異常。
CloneNotSupportedException    當呼叫 Object 類中的 clone 方法克隆物件,但該物件的類無法實現 Cloneable 介面時,丟擲該異常。
IllegalAccessException    拒絕訪問一個類的時候,丟擲該異常。
InstantiationException    當試圖使用 Class 類中的 newInstance 方法建立一個類的例項,而指定的類物件因為是一個介面或是一個抽象類而無法例項化時,丟擲該異常。
InterruptedException    一個執行緒被另一個執行緒中斷,丟擲該異常。
NoSuchFieldException    請求的變數不存在
NoSuchMethodException    請求的方法不存在
異常處理基本用法
在編寫程式碼處理異常時,對於檢查性異常,有2種不同的處理方式:

使用try{}catch{}finally語句塊處理它
在函式簽名中使用 throws 宣告交給函式呼叫者去解決
try{}catch{}finally{} 用法
try{
     //try塊中放可能發生異常的程式碼。
     //如果執行完try且不發生異常,則接著去執行finally塊和finally後面的程式碼
     //如果發生異常,則嘗試去匹配catch塊。
 
}catch(SQLException SQLexception){
    //每一個catch塊用於捕獲並處理一個特定的異常,或者這異常型別的子類。Java7中可以將多個異常宣告在一個catch中。
    //catch後面的括號定義了異常型別和異常引數。如果異常與之匹配且是最先匹配到的,則虛擬機器將使用這個catch塊來處理異常。
    //在catch塊中可以使用這個塊的異常引數來獲取異常的相關資訊。異常引數是這個catch塊中的區域性變數,其它塊不能訪問。
    //如果當前try塊中發生的異常在後續的所有catch中都沒捕獲到,則先去執行finally,然後到這個函式的外部caller中去匹配異常處理器。
    //如果try中沒有發生異常,則所有的catch塊將被忽略。
 
}catch(Exception exception){
    //...
}finally{
 
    //finally塊通常是可選的。
   //無論異常是否發生,異常是否匹配被處理,finally都會執行。
   //一個try至少要有一個catch塊,否則, 至少要有1個finally塊。但是finally不是用來處理異常的,finally不會捕獲異常。
  //finally主要做一些清理工作,如流的關閉,資料庫連線的關閉等。 
}

需要注意的點:

1、try塊中的區域性變數和catch塊中的區域性變數(包括異常變數),以及finally中的區域性變數,它們之間不可共享使用。

2、每一個catch塊用於處理一個異常。異常匹配是按照catch塊的順序從上往下尋找的,只有第一個匹配的catch會得到執行。匹配時,不僅執行精確匹配,也支援父類匹配,因此,如果同一個try塊下的多個catch異常型別有父子關係,應該將子類異常放在前面,父類異常放在後面,這樣保證每個catch塊都有存在的意義。

3、java中,異常處理的任務就是將執行控制流從異常發生的地方轉移到能夠處理這種異常的地方去。也就是說:當一個函式的某條語句發生異常時,這條語句的後面的語句不會再執行,它失去了焦點。執行流跳轉到最近的匹配的異常處理catch程式碼塊去執行,異常被處理完後,執行流會在“這個異常的catch程式碼塊”後面接著執行。

public static void main(String[] args){
        try {
            divideOperation();
        }catch(ArithmeticException ae) {
            System.out.println("異常捕獲,處理異常");
        }
        System.out.println("我是catch塊後邊的程式,異常處理後,我繼續執行");
    }
    public static void divideOperation(){
        int a = 5/0;  //異常丟擲點
        System.out.println("出現異常啦,方法被終止了,我躺著中槍了"); //不會執行
    }

結果:

異常捕獲,處理異常
我是catch塊後邊的程式,異常處理後,我繼續執行

throws/throw 用法
如果一個方法沒有捕獲一個檢查性異常,那麼該方法必須使用 throws 關鍵字來宣告。throws 關鍵字放在方法簽名的尾部。
也可以使用 throw 關鍵字丟擲一個異常,無論它是新例項化的還是剛捕獲到的。

下面方法的宣告丟擲一個 RemoteException 異常:

public class ThrowsDemo
{
  public void deposit(double amount) throws RemoteException
  {
    // 方法內容
    throw new RemoteException();
  }
  
}

一個方法可以宣告丟擲多個異常,多個異常之間用逗號隔開。
例如,下面的方法宣告丟擲 RemoteException 和 InsufficientFundsException:

public class ThrowsDemo2
{
   public void withdraw(double amount) throws RemoteException,
                              InsufficientFundsException
   {
      //方法內容
   }
   
}

finally關鍵字
finally 關鍵字用來建立在 try 程式碼塊後面執行的程式碼塊。
無論是否發生異常,finally 程式碼塊中的程式碼總會被執行,通常用來做資源釋放操作:關閉檔案,關閉資料庫連線等等。

1、不要在finally中丟擲異常,finally塊沒有處理異常的能力,處理異常的只能是catch塊。

2、在同一try…catch…finally塊中 ,如果try中丟擲異常,且有匹配的catch塊,則先執行catch塊,再執行finally塊。如果沒有catch塊匹配,則先執行finally,然後去外面的呼叫者中尋找合適的catch塊。

3、在同一try…catch…finally塊中 ,try發生異常,且匹配的catch塊中處理異常時也丟擲異常,那麼後面的finally也會執行:首先執行finally塊,然後去外圍呼叫者中尋找合適的catch塊。

4、不要在fianlly中使用return,否則會覆蓋返回值

5、減輕finally的任務,不要在finally中做一些其它的事情,finally塊僅僅用來釋放資源是最合適的。

6、儘量將所有的return寫在函式的最後面,而不是try … catch … finally中。

自定義異常
在 Java 中你可以自定義異常,編寫自己的異常類時需要記住下面的幾點:

所有異常都必須是 Throwable 的子類。
如果希望寫一個檢查性異常類,則需要繼承 Exception 類。
如果你想寫一個執行時異常類,那麼需要繼承 RuntimeException 類。
可以像下面這樣定義自己的異常類:

class MyException extends Exception{
}

只繼承Exception 類來建立的異常類是檢查性異常類。

下面的 InsufficientFundsException 類是使用者定義的異常類,它繼承自 Exception。
一個異常類和其它任何類一樣,包含有變數和方法。

以下例項是一個銀行賬戶的模擬,通過銀行卡的號碼完成識別,可以進行存錢和取錢的操作。

package com.lkf.basic;
//自定義異常類,繼承Exception類
public class InsufficientFundsException extends Exception
{
    //賬戶餘額
    private double amount;
    public InsufficientFundsException(double amount)
    {
        this.amount = amount;
    }
    public double getAmount()
    {
        return amount;
    }
}



為了展示如何使用我們自定義的異常類,
在下面的 CheckingAccount 類中包含一個 withdraw() 方法丟擲一個 InsufficientFundsException 異常。

package com.lkf.basic;

//此類模擬銀行賬戶
public class CheckingAccount {
    //balance為餘額,
    private double balance;
    //number為卡號
    private int number;

    public CheckingAccount(int number) {
        this.number = number;
    }

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

    //方法:取錢
    public void withdraw(double amount) throws
            InsufficientFundsException {
        if (amount <= balance) {
            balance -= amount;
        } else {
            double needs = amount - balance;
            throw new InsufficientFundsException(needs);
        }
    }

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

    //方法:返回卡號
    public int getNumber() {
        return number;
    }
}

下面的 BankDemo 程式示範瞭如何呼叫 CheckingAccount 類的 deposit() 和 withdraw() 方法。

package com.lkf.basic;

public class BankDemo {
    public static void main(String[] args) {
        CheckingAccount c = new CheckingAccount(101);
        System.out.println("存錢 500 元....");
        c.deposit(500.00);
        try {
            System.out.println("\n取錢 100 元...");
            c.withdraw(100.00);
            System.out.println("\n取錢 600 元...");
            c.withdraw(600.00);
        } catch (InsufficientFundsException e) {
            System.out.println("很抱歉您的餘額不足,差:"
                    + e.getAmount());
            e.printStackTrace();
        }
    }
}


結果:

存錢 500 元....

取錢 100 元...

取錢 600 元...
很抱歉您的餘額不足,差:200.0
com.lkf.basic.InsufficientFundsException
    at com.lkf.basic.CheckingAccount.withdraw(CheckingAccount.java:26)
    at com.lkf.basic.BankDemo.main(BankDemo.java:12)
--------------------- 
作者:IT碼客 
來源:CSDN 
原文:https://blog.csdn.net/u010647035/article/details/8038