天天看點

java異常處理機制

java異常處理機制

Java 中的異常(Exception)又稱為例外,是一個在程式執行期間發生的事件,它中斷正在執行的程式的正常指令流。為了能夠及時有效地處理程式中的運作錯誤,必須使用異常類。

異常簡介

在程式中,錯誤可能産生于程式員沒有預料到的各種情況,或者超出程式員可控範圍的環境,例如使用者的壞資料、試圖打開一個不存在的檔案等。為了能夠及時有效地處理程式中的運作錯誤,Java 專門引入了異常類。

例 1

為了更好地了解什麼是異常,下面來看一段非常簡單的 Java 程式。下面的示例代碼實作了允許使用者輸入 1~3 以内的整數,其他情況提示輸入錯誤。

複制代碼

package ch11;

import Java.util.Scanner;

public class TestO1

{

public static void main(String[] args)
{
    System.out.println("請輸入您的選擇:(1~3 之間的整數)");
    Scanner input=new Scanner(System.in);
    int num=input.nextInt();
    switch(num)
    {
        case 1:
            System.out.println("one");
            break;
        case 2:
            System.out.println("two");
            break;
        case 3:
            System.out.println("three");
            break;
        default:
            System.out.println("error");
            break;
    }
}           

}

正常情況下,使用者會按照系統的提示輸入 1~3 之間的數字。但是,如果使用者沒有按要求進行輸入,例如輸入了一個字母“a”,則程式在運作時将會發生異常,運作結果如下所示。

請輸入您的選擇:(1~3 之間的整數)

a

Exception in thread "main" java.util.InputMismatchException

at java.util.Scanner.throwFor(Unknown Source)

at java.util.Scanner.next(Unknown Source)

at java.util.Scanner.nextInt(Unknown Source)

at text.text.main(text.java:11)

異常産生的原因及使用原則

在 Java 中一個異常的産生,主要有如下三種原因:

Java 内部錯誤發生異常,Java 虛拟機産生的異常。

編寫的程式代碼中的錯誤所産生的異常,例如空指針異常、數組越界異常等。這種異常稱為未檢査的異常,一般需要在某些類中集中處理這些異常。

通過 throw 語句手動生成的異常,這種異常稱為檢査的異常,一般用來告知該方法的調用者一些必要的資訊。

Java 通過面向對象的方法來處理異常。在一個方法的運作過程中,如果發生了異常,則這個方法會産生代表該異常的一個對象,并把它交給運作時的系統,運作時系統尋找相應的代碼來處理這一異常。

我們把生成異常對象,并把它送出給運作時系統的過程稱為拋出(throw)異常。運作時系統在方法的調用棧中查找,直到找到能夠處理該類型異常的對象,這一個過程稱為捕獲(catch)異常。

Java 異常強制使用者考慮程式的強健性和安全性。異常處理不應用來控制程式的正常流程,其主要作用是捕獲程式在運作時發生的異常并進行相應處理。編寫代碼處理某個方法可能出現的異常,可遵循如下三個原則:

在目前方法聲明中使用 try catch 語句捕獲異常。

一個方法被覆寫時,覆寫它的方法必須拋出相同的異常或異常的子類。

如果父類抛出多個異常,則覆寫方法必須拋出那些異常的一個子集,而不能拋出新異常。

異常類型

在 Java 中所有異常類型都是内置類 java.lang.Throwable 類的子類,即 Throwable 位于異常類層次結構的頂層。Throwable 類下有兩個異常分支 Exception 和 Error,如圖 1 所示。

圖1 異常結構圖

由圖 2 可以知道,Throwable 類是所有異常和錯誤的超類,下面有 Error 和 Exception 兩個子類分别表示錯誤和異常。其中異常類 Exception 又分為運作時異常和非運作時異常,這兩種異常有很大的差別,也稱為不檢查異常(Unchecked Exception)和檢查異常(Checked Exception)。

Exception 類用于使用者程式可能出現的異常情況,它也是用來建立自定義異常類型類的類。

Error 定義了在通常環境下不希望被程式捕獲的異常。Error 類型的異常用于 Java 運作時由系統顯示與運作時系統本身有關的錯誤。堆棧溢出是這種錯誤的一例。

本章不讨論關于 Error 類型的異常處理,因為它們通常是災難性的緻命錯誤,不是程式可以控制的。本章接下來的内容将讨論 Exception 類型的異常處理。

運作時異常都是 RuntimeException 類及其子類異常,如 NullPointerException、IndexOutOfBoundsException 等,這些異常是不檢查異常,程式中可以選擇捕獲處理,也可以不處理。這些異常一般由程式邏輯錯誤引起,程式應該從邏輯角度盡可能避免這類異常的發生。

非運作時異常是指 RuntimeException 以外的異常,類型上都屬于 Exception 類及其子類。從程式文法角度講是必須進行處理的異常,如果不處理,程式就不能編譯通過。如 IOException、ClassNotFoundException 等以及使用者自定義的 Exception 異常,一般情況下不自定義檢查異常。表 1 列出了一些常見的異常類型及它們的作用。

表1 Java中常見的異常類型

異常類型 說明

Exception 異常層次結構的根類

RuntimeException 運作時異常,多數 java.lang 異常的根類

ArithmeticException 算術譜誤異常,如以零做除數

ArraylndexOutOfBoundException 數組大小小于或大于實際的數組大小

NullPointerException 嘗試通路 null 對象成員,空指針異常

ClassNotFoundException 不能加載所需的類

NumberF ormatException 數字轉化格式異常,比如字元串到 float 型數字的轉換無效

IOException I/O 異常的根類

F ileN otF oundException 找不到檔案

EOFException 檔案結束

InterruptedException 線程中斷

IllegalArgumentException 方法接收到非法參數

ClassCastException 類型轉換異常

SQLException 操作資料庫異常

在java應用程式中,有兩種異常處理機制:抛出異常、捕獲異常。

聲明異常抛出異常:

  當一個方法出現錯誤引發異常時,方法建立異常對象傳遞運作時系統,異常對象中包含了異常類型和異常出現時程式的狀态等異常資訊。運作時系統負責尋找處置異常的代碼并執行。

  可以通過 throws 關鍵字在方法上聲明該方法要拋出的異常,然後在方法内部通過 throw 拋出異常對象。本節詳細介紹在 Java 中如何聲明異常和拋出異常。

throws 關鍵字和 throw 關鍵字在使用上的幾點差別如下:

throws 用來聲明一個方法可能抛出的所有異常資訊,throw 則是指拋出的一個具體的異常類型。

通常在一個方法(類)的聲明處通過 throws 聲明方法(類)可能拋出的異常資訊,而在方法(類)内部通過 throw 聲明一個具體的異常資訊。

throws 通常不用顯示地捕獲異常,可由系統自動将所有捕獲的異常資訊抛給上級方法; throw 則需要使用者自己捕獲相關的異常,而後再對其進行相關包裝,最後将包裝後的異常資訊抛出。

throws 聲明異常

  當一個方法産生一個它不處理的異常時,那麼就需要在該方法的頭部聲明這個異常,以便将該異常傳遞到方法的外部進行處理。可以使用 throws 關鍵字在方法的頭部聲明一個異常,其具體格式如下:

returnType method_name(paramList) throws Exception 1,Exception2,…{…}

其中,returnType 表示傳回值類型,method_name 表示方法名,Exception 1,Exception2,… 表示異常類。如果有多個異常類,它們之間用逗号分隔。這些異常類可以是方法中調用了可能拋出異常的方法而産生的異常,也可以是方法體中生成并拋出的異常。

建立一個 readFile() 方法,該方法用于讀取檔案内容,在讀取的過程中可能會産生 IOException 異常,但是在該方法中不做任何的處理,而将可能發生的異常交給調用者處理。在 main() 方法中使用 try catch 捕獲異常,并輸出異常資訊。代碼如下:

import java.io.FileInputStream;

import java.io.IOException;

public class Test04

public void readFile() throws IOException
{
    //定義方法時聲明異常
    FileInputStream file=new FileInputStream("read.txt");    //創達 FileInputStream 執行個體對象
    int f;
    while((f=file.read())!=-1)
    {
        System.out.println((char)f);
        f=file.read();
    }
    file.close();
}
public static void main(String[] args)
{
    Throws t=new Test04();
    try
    {
        t.readFile();    //調用 readFHe()方法
    }
    catch(IOException e)
    {    //捕獲異常
        System.out.println(e);
    }
}           

以上代碼,首先在定義 readFile() 方法時用 throws 關鍵字聲明在該方法中可能産生的異常,然後在 main() 方法中調用 readFile() 方法,并使用 catch 語句捕獲産生的異常。

注意:在編寫類繼承代碼時要注意,子類在覆寫父類帶 throws 子句的方法時,子類的方法聲明中的 throws 子句不能出現父類對應方法的 throws 子句中沒有的異常類型,是以 throws 子句可以限制子類的行為。也就是說,子類方法拋出的異常不會超過父類定義的範圍。

throw 拋出異常

throw 語句用來直接拋出一個異常,後接一個可拋出的異常類對象,其文法格式如下:

throw ExceptionObject;

其中,ExceptionObject 必須是 Throwable 類或其子類的對象。如果是自定義異常類,也必須是 Throwable 的直接或間接子類。例如,以下語句在編譯時将會産生文法錯誤:

throw new String("拋出異常"); //因為String類不是Throwable類的子類

當 throw 語句執行時,它後面的語句将不執行,此時程式轉向調用者程式,尋找與之相比對的 catch 語句,執行相應的異常處理程式。如果沒有找到相比對的 catch 語句,則再轉向上一層的調用程式。這樣逐層向上,直到最外層的異常處理程式終止程式并列印出調用棧情況。

例 2

在某倉庫管理系統中,要求管理者的使用者名需要由 8 位以上的字母或者數字組成,不能含有其他的字元。當長度在 8 位以下時拋出異常,并顯示異常資訊;當字元含有非字母或者數字時,同樣拋出異常,顯示異常資訊。代碼如下:

import java.util.Scanner;

public class Test05

public boolean validateUserName(String username)
{
    boolean con=false;
    if(username.length()>8)
    {    //判斷使用者名長度是否大于8位
        for(int i=0;i<username.length();i++)
        {
            char ch=username.charAt(i);    //擷取每一位字元
            if((ch>='0'&&ch<='9')||(ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z'))
            {
                con=true;
            }
            else
            {
                con=false;
                throw new IllegalArgumentException("使用者名隻能由字母和數字組成!"");
            }
        }
    }
    else
    {
        throw new IllegalArgumentException("使用者名長度必須大于 8 位!");
    }
    return con;
}
public static void main(String[] args)
{
    Test05 te=new Test05();
    Scanner input=new Scanner(System.in);
    System.out.println("請輸入使用者名:");
    String username=input.next();
    try
    {
        boolean con=te.validateUserName(username);
        if(con)
        {
            System.out.println("使用者名輸入正确!");
        }
    }
    catch(IllegalArgumentException e)
    {
        System.out.println(e);
    }
}           

如上述代碼,在 validateUserName() 方法中兩處拋出了 IllegalArgumentException 異常,即當使用者名字元含有非字母或者數字以及長度不夠 8 位時。在 main() 方法中,調用了 validateUserName() 方法,并使用 catch 語句捕獲該方法可能拋出的異常。

運作程式,當使用者輸入的使用者名包含非字母或者數字的字元時,程式輸出異常資訊,如下所示。

請輸入使用者名:

administrator@#

java.lang.IllegalArgumentException: 使用者名隻能由字母和數字組成!

當使用者輸入的使用者名長度不夠 8 位時,程式同樣會輸出異常資訊,如下所示。

admin

java.lang.IllegalArgumentException: 使用者名長度必須大于 8 位!

捕獲異常:

  在方法抛出異常後,運作時系統将轉為尋找合适的異常處理器。潛在的異常處理器是異常發生時依次存存留在調用棧中的方法的集合。當異常處理器所能處理的異常類型與方法抛出的異常類型相符時,即為合适的異常處理器。運作時系統從發生異常的方法開始,依次回查調用棧中的方法,直至找到合适的異常處理器的方法并執行。當運作時系統周遊調用棧,而未找到合适的異常處理器方法時,運作時系統終止,同時意味着java程式的終止。

通常使用try、catch、finally來捕獲異常:

try

邏輯代碼塊           

catch(ExceptionType e)

異常處理代碼塊           

finally

清理代碼塊           

try塊:用于捕獲異常。其後可接零個或多個catch塊,如果沒有catch塊,則必須跟個finally塊。

catch塊:用于處理try捕獲到的異常。

finally塊:無論是否捕獲或處理異常,finally塊裡的語句都會被執行。當在try塊或catch塊中遇到return語句時,finally 語句塊将在方法傳回之前被執行。

在以下4種特殊情況下,finally塊不會被執行:

1)在finally語句塊中發生了異常。

2)在前面的代碼中用了System. exit(退出程式。

3)程式所在的線程死亡。

4)關閉CPU。

try、catch、finally 語句塊的執行順序:

1)當try沒有捕獲到異常時: try 語句塊中的語句逐一被執行,程式将跳過catch語句塊,執行finally語句塊和其後的語句;

2)當try捕獲到異常,catch語句塊裡沒有處理此異常的情況:當try語句塊裡的某條語句出現異常時,而沒有處理此異常的catch語句塊時,此異常将會抛給JVM處理,finally 語句塊裡的語句還是會被執行,但finally語句塊後的語句不會被執行;

3)當try捕獲到異常,catch 語句塊裡有處理此異常的情況:在try語句塊中是按照順序來執行的,當執行到某一條語句出現異常時,程式将跳到catch語句塊,并與catch語句塊逐一比對,找到與之對應的處理程式,其他的catch語句塊将不會被執行,而try語句塊中,出現異常之後的語句也不會被執行,catch 語句塊執行完後,執行finally語句塊裡的語句,最後執行finally語句塊後的語句。

原文位址

https://www.cnblogs.com/l199616j/p/11297326.html