第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

全部開(kāi)發(fā)者教程

Java 異常處理

Java 的異常處理是 Java 語(yǔ)言的一大重要特性,也是提高代碼健壯性的最強(qiáng)大方法之一。當(dāng)我們編寫了錯(cuò)誤的代碼時(shí),編譯器在編譯期間可能會(huì)拋出異常,有時(shí)候即使編譯正常,在運(yùn)行代碼的時(shí)候也可能會(huì)拋出異常。本小節(jié)我們將介紹什么是異常、Java 中異常類的架構(gòu)、如何進(jìn)行異常處理、如何自定義異常、什么是異常鏈如何使用異常鏈等內(nèi)容。

1. 什么是異常

異常就是程序上的錯(cuò)誤,我們?cè)诰帉懗绦虻臅r(shí)候經(jīng)常會(huì)產(chǎn)生錯(cuò)誤,這些錯(cuò)誤劃分為編譯期間的錯(cuò)誤運(yùn)行期間的錯(cuò)誤。

下面我們來(lái)看幾個(gè)常見(jiàn)的異常案例。

如果語(yǔ)句漏寫分號(hào),程序在編譯期間就會(huì)拋出異常,實(shí)例如下:

public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello World!")
    }
}

運(yùn)行結(jié)果:

$ javac Hello.java
Hello.java:3: 錯(cuò)誤: 需要';'
        System.out.println("Hello World!")
                                          ^
1 個(gè)錯(cuò)誤

運(yùn)行過(guò)程:

由于代碼的第 3 行語(yǔ)句漏寫了分號(hào),Java 編譯器給出了明確的提示。

static 關(guān)鍵字寫成了 statci,實(shí)例如下:

Hello.java:2: 錯(cuò)誤: 需要<標(biāo)識(shí)符>
    public statci void main(String[] args) {
                 ^
1 個(gè)錯(cuò)誤

當(dāng)數(shù)組下標(biāo)越界,程序在編譯階段不會(huì)發(fā)生錯(cuò)誤,但在運(yùn)行時(shí)會(huì)拋出異常。實(shí)例如下:

public class ArrayOutOfIndex {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3};
        System.out.println(arr[3]);
    }
}

運(yùn)行結(jié)果:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
	at ArrayOutOfIndex.main(ArrayOutOfIndex.java:4)

運(yùn)行過(guò)程:

2. Java 異常類架構(gòu)

在 Java 中,通過(guò) Throwable 及其子類來(lái)描述各種不同類型的異常。如下是 Java 異常類的架構(gòu)圖(不是全部,只展示部分類):

2.1 Throwable 類

Throwable 位于 java.lang 包下,它是 Java 語(yǔ)言中所有錯(cuò)誤(Error)和異常(Exception)的父類。

Throwable 包含了其線程創(chuàng)建時(shí)線程執(zhí)行堆棧的快照,它提供了 printStackTrace() 等接口用于獲取堆棧跟蹤數(shù)據(jù)等信息。

主要方法:

  • fillInStackTrace: 用當(dāng)前的調(diào)用棧層次填充 Throwable 對(duì)象棧層次,添加到棧層次任何先前信息中;

  • getMessage:返回關(guān)于發(fā)生的異常的詳細(xì)信息。這個(gè)消息在 Throwable 類的構(gòu)造函數(shù)中初始化了;

  • getCause:返回一個(gè) Throwable 對(duì)象代表異常原因;

  • getStackTrace:返回一個(gè)包含堆棧層次的數(shù)組。下標(biāo)為 0 的元素代表?xiàng)m?,最后一個(gè)元素代表方法調(diào)用堆棧的棧底;

  • printStackTrace:打印 toString() 結(jié)果和棧層次到 System.err,即錯(cuò)誤輸出流。

2.2 Error 類

ErrorThrowable 的一個(gè)直接子類,它可以指示合理的應(yīng)用程序不應(yīng)該嘗試捕獲的嚴(yán)重問(wèn)題。這些錯(cuò)誤在應(yīng)用程序的控制和處理能力之外,編譯器不會(huì)檢查 Error,對(duì)于設(shè)計(jì)合理的應(yīng)用程序來(lái)說(shuō),即使發(fā)生了錯(cuò)誤,本質(zhì)上也無(wú)法通過(guò)異常處理來(lái)解決其所引起的異常狀況。

常見(jiàn) Error

  • AssertionError:斷言錯(cuò)誤;

  • VirtualMachineError:虛擬機(jī)錯(cuò)誤;

  • UnsupportedClassVersionError:Java 類版本錯(cuò)誤;

  • OutOfMemoryError :內(nèi)存溢出錯(cuò)誤。

2.3 Exception 類

ExceptionThrowable 的一個(gè)直接子類。它指示合理的應(yīng)用程序可能希望捕獲的條件。

Exception 又包括 Unchecked Exception(非檢查異常)和 Checked Exception(檢查異常)兩大類別。

2.3.1 Unchecked Exception (非檢查異常)

Unchecked Exception 是編譯器不要求強(qiáng)制處理的異常,包含 RuntimeException 以及它的相關(guān)子類。我們編寫代碼時(shí)即使不去處理此類異常,程序還是會(huì)編譯通過(guò)。

常見(jiàn)非檢查異常:

  • NullPointerException:空指針異常;

  • ArithmeticException:算數(shù)異常;

  • ArrayIndexOutOfBoundsException:數(shù)組下標(biāo)越界異常;

  • ClassCastException:類型轉(zhuǎn)換異常。

2.3.2 Checked Exception(檢查異常)

Checked Exception 是編譯器要求必須處理的異常,除了 RuntimeException 以及它的子類,都是 Checked Exception 異常。我們?cè)诔绦蚓帉憰r(shí)就必須處理此類異常,否則程序無(wú)法編譯通過(guò)。

常見(jiàn)檢查異常:

  • IOException:IO 異常

  • SQLException:SQL 異常

3. 如何進(jìn)行異常處理

在 Java 語(yǔ)言中,異常處理機(jī)制可以分為兩部分:

  1. 拋出異常:當(dāng)一個(gè)方法發(fā)生錯(cuò)誤時(shí),會(huì)創(chuàng)建一個(gè)異常對(duì)象,并交給運(yùn)行時(shí)系統(tǒng)處理;

  2. 捕獲異常:在方法拋出異常之后,運(yùn)行時(shí)系統(tǒng)將轉(zhuǎn)為尋找合適的異常處理器。

Java 通過(guò) 5 個(gè)關(guān)鍵字來(lái)實(shí)現(xiàn)異常處理,分別是:throw、throws、try、catchfinally。

異常總是先拋出,后捕獲的。下面我們將圍繞著 5 個(gè)關(guān)鍵字來(lái)詳細(xì)講解如何拋出異常以及如何捕獲異常。

4. 拋出異常

4.1 實(shí)例

我們先來(lái)看一個(gè)除零異常的實(shí)例代碼:

public class ExceptionDemo1 {
    // 打印 a / b 的結(jié)果
    public static void divide(int a, int b) {
        System.out.println(a / b);
    }

    public static void main(String[] args) {
        // 調(diào)用 divide() 方法
        divide(2, 0);
    }
}

運(yùn)行結(jié)果:

Exception in thread "main" java.lang.ArithmeticException: / by zero
	at ExceptionDemo1.divide(ExceptionDemo1.java:4)
	at ExceptionDemo1.main(ExceptionDemo1.java:9)

運(yùn)行過(guò)程:

我們知道 0 是不能用作除數(shù)的,由于 divide() 方法中除數(shù) b0,所以代碼將停止執(zhí)行并顯示了相關(guān)的異常信息,此信息為堆棧跟蹤,上面的運(yùn)行結(jié)果告訴我們:main 線程發(fā)生了類型為 ArithmeticException 的異常,顯示消息為 by zero,并且提示了可能發(fā)生異常的方法和行號(hào)。

4.2 throw

上面的實(shí)例中,程序在運(yùn)行時(shí)引發(fā)了錯(cuò)誤,那么如何來(lái)顯示拋出(創(chuàng)建)異常呢?

我們可以使用 throw 關(guān)鍵字來(lái)拋出異常,throw 關(guān)鍵字后面跟異常對(duì)象,改寫上面的實(shí)例代碼:

public class ExceptionDemo2 {
    // 打印 a / b 的結(jié)果
    public static void divide(int a, int b) {
        if (b == 0) {
            // 拋出異常
            throw new ArithmeticException("除數(shù)不能為零");
        }
        System.out.println(a / b);
    }

    public static void main(String[] args) {
        // 調(diào)用 divide() 方法
        divide(2, 0);
    }
}

運(yùn)行結(jié)果:

Exception in thread "main" java.lang.ArithmeticException: 除數(shù)不能為零
	at ExceptionDemo2.divide(ExceptionDemo2.java:5)
	at ExceptionDemo2.main(ExceptionDemo2.java:12)

運(yùn)行過(guò)程:

代碼在運(yùn)行時(shí)同樣引發(fā)了錯(cuò)誤,但顯示消息為 “除數(shù)不能為零”。我們看到 divide() 方法中加入了條件判斷,如果調(diào)用者將參數(shù) b 設(shè)置為 0 時(shí),會(huì)使用 throw 關(guān)鍵字來(lái)拋出異常,throw 后面跟了一個(gè)使用 new 關(guān)鍵字實(shí)例化的算數(shù)異常對(duì)象,并且將消息字符串作為參數(shù)傳遞給了算數(shù)異常的構(gòu)造函數(shù)。

我們可以使用 throw 關(guān)鍵字拋出任何類型的 Throwable 對(duì)象,它會(huì)中斷方法,throw 語(yǔ)句之后的所有內(nèi)容都不會(huì)執(zhí)行。除非已經(jīng)處理拋出的異常。異常對(duì)象不是從方法中返回的,而是從方法中拋出的。

4.3 throws

可以通過(guò) throws 關(guān)鍵字聲明方法要拋出何種類型的異常。如果一個(gè)方法可能會(huì)出現(xiàn)異常,但是沒(méi)有能力處理這種異常,可以在方法聲明處使用 throws 關(guān)鍵字來(lái)聲明要拋出的異常。例如,汽車在運(yùn)行時(shí)可能會(huì)出現(xiàn)故障,汽車本身沒(méi)辦法處理這個(gè)故障,那就讓開(kāi)車的人來(lái)處理。

throws 用在方法定義時(shí)聲明該方法要拋出的異常類型,如下是偽代碼:

public void demoMethod() throws Exception1, Exception2, ... ExceptionN {
    // 可能產(chǎn)生異常的代碼
}

throws 后面跟的異常類型列表可以有一個(gè)也可以有多個(gè),多個(gè)則以 , 分割。當(dāng)方法產(chǎn)生異常列表中的異常時(shí),將把異常拋向方法的調(diào)用方,由調(diào)用方處理。

throws 有如下使用規(guī)則:

  1. 如果方法中全部是非檢查異常(即 Error、RuntimeException 以及的子類),那么可以不使用 throws 關(guān)鍵字來(lái)聲明要拋出的異常,編譯器能夠通過(guò)編譯,但在運(yùn)行時(shí)會(huì)被系統(tǒng)拋出;
  2. 如果方法中可能出現(xiàn)檢查異常,就必須使用 throws 聲明將其拋出或使用 try catch 捕獲異常,否則將導(dǎo)致編譯錯(cuò)誤;
  3. 當(dāng)一個(gè)方法拋出了異常,那么該方法的調(diào)用者必須處理或者重新拋出該異常;
  4. 當(dāng)子類重寫父類拋出異常的方法時(shí),聲明的異常必須是父類所聲明異常的同類或子類。

5. 捕獲異常

使用 try 和 catch 關(guān)鍵字可以捕獲異常。try catch 代碼塊放在異??赡馨l(fā)生的地方。它的語(yǔ)法如下:

try {
    // 可能會(huì)發(fā)生異常的代碼塊
} catch (Exception e1) {
    // 捕獲并處理try拋出的異常類型Exception
} catch (Exception2 e2) {
    // 捕獲并處理try拋出的異常類型Exception2
} finally {
    // 無(wú)論是否發(fā)生異常,都將執(zhí)行的代碼塊
}

我們來(lái)看一下上面語(yǔ)法中的 3 種語(yǔ)句塊:

  1. try 語(yǔ)句塊:用于監(jiān)聽(tīng)異常,當(dāng)發(fā)生異常時(shí),異常就會(huì)被拋出;
  2. catch 語(yǔ)句塊catch 語(yǔ)句包含要捕獲的異常類型的聲明,當(dāng) try 語(yǔ)句塊發(fā)生異常時(shí),catch 語(yǔ)句塊就會(huì)被檢查。當(dāng) catch 塊嘗試捕獲異常時(shí),是按照 catch 塊的聲明順序從上往下尋找的,一旦匹配,就不會(huì)再向下執(zhí)行。因此,如果同一個(gè) try 塊下的多個(gè) catch 異常類型有父子關(guān)系,應(yīng)該將子類異常放在前面,父類異常放在后面;
  3. finally 語(yǔ)句塊:無(wú)論是否發(fā)生異常,都會(huì)執(zhí)行 finally 語(yǔ)句塊。finally 常用于這樣的場(chǎng)景:由于 finally 語(yǔ)句塊總是會(huì)被執(zhí)行,所以那些在 try 代碼塊中打開(kāi)的,并且必須回收的物理資源(如數(shù)據(jù)庫(kù)連接、網(wǎng)絡(luò)連接和文件),一般會(huì)放在 finally 語(yǔ)句塊中釋放資源。

try 語(yǔ)句塊后可以接零個(gè)或多個(gè) catch 語(yǔ)句塊,如果沒(méi)有 catch 塊,則必須跟一個(gè) finally 語(yǔ)句塊。簡(jiǎn)單來(lái)說(shuō),try 不允許單獨(dú)使用,必須和 catchfinally 組合使用,catchfinally 也不能單獨(dú)使用。

實(shí)例如下:

public class ExceptionDemo3 {
    // 打印 a / b 的結(jié)果
    public static void divide(int a, int b) {
        System.out.println(a / b);
    }

    public static void main(String[] args) {
        try {
            // try 語(yǔ)句塊
            // 調(diào)用 divide() 方法
            divide(2, 0);
        } catch (ArithmeticException e) {
            // catch 語(yǔ)句塊
            System.out.println("catch: 發(fā)生了算數(shù)異常:" + e);
        } finally {
            // finally 語(yǔ)句塊
            System.out.println("finally: 無(wú)論是否發(fā)生異常,都會(huì)執(zhí)行");
        }
    }
}

運(yùn)行結(jié)果:

catch: 發(fā)生了算數(shù)異常:java.lang.ArithmeticException: / by zero
finally: 無(wú)論是否發(fā)生異常,都會(huì)執(zhí)行

運(yùn)行過(guò)程:

divide() 方法中除數(shù) b0,會(huì)發(fā)生除零異常,我們?cè)诜椒ㄕ{(diào)用處使用了 try 語(yǔ)句塊對(duì)異常進(jìn)行捕獲;如果捕獲到了異常, catch 語(yǔ)句塊會(huì)對(duì) ArithmeticException 類型的異常進(jìn)行處理,此處打印了一行自定義的提示語(yǔ)句;最后的 finally 語(yǔ)句塊,無(wú)論發(fā)生異常與否,總會(huì)執(zhí)行。

Java 7 以后,catch 多種異常時(shí),也可以像下面這樣簡(jiǎn)化代碼:

try {
    // 可能會(huì)發(fā)生異常的代碼塊
} catch (Exception | Exception2 e) {
    // 捕獲并處理try拋出的異常類型
} finally {
    // 無(wú)論是否發(fā)生異常,都將執(zhí)行的代碼塊
}

6. 自定義異常

自定義異常,就是定義一個(gè)類,去繼承 Throwable 類或者它的子類。

Java 內(nèi)置了豐富的異常類,通常使用這些內(nèi)置異常類,就可以描述我們?cè)诰幋a時(shí)出現(xiàn)的大部分異常情況。一旦內(nèi)置異常無(wú)法滿足我們的業(yè)務(wù)要求,就可以通過(guò)自定義異常描述特定業(yè)務(wù)產(chǎn)生的異常類型。

實(shí)例:

public class ExceptionDemo4 {

    static class MyCustomException extends RuntimeException {
        /**
         * 無(wú)參構(gòu)造方法
         */
        public MyCustomException() {
            super("我的自定義異常");
        }
    }

    public static void main(String[] args) {
      	// 直接拋出異常
        throw new MyCustomException();
    }
}

運(yùn)行結(jié)果:

Exception in thread "main" ExceptionDemo4$MyCustomException: 我的自定義異常
	at ExceptionDemo4.main(ExceptionDemo4.java:13)

運(yùn)行過(guò)程:

在代碼中寫了一個(gè)自定義異常 MyCustomException,繼承自 RuntimeException,它是一個(gè)靜態(tài)內(nèi)部類,這樣在主方法中就可以直接拋出這個(gè)異常類了。當(dāng)然,也可以使用 catch 來(lái)捕獲此類型異常。

7. 異常鏈

異常鏈?zhǔn)且砸粋€(gè)異常對(duì)象為參數(shù)構(gòu)造新的異常對(duì)象,新的異常對(duì)象將包含先前異常的信息。簡(jiǎn)單來(lái)說(shuō),就是將異常信息從底層傳遞給上層,逐層拋出,我們來(lái)看一個(gè)實(shí)例:

public class ExceptionDemo5 {

    /**
     * 第一個(gè)自定義的靜態(tài)內(nèi)部異常類
     */
    static class FirstCustomException extends Exception {

        // 無(wú)參構(gòu)造方法
        public FirstCustomException() {
            super("第一個(gè)異常");
        }
    }

    /**
     * 第二個(gè)自定義的靜態(tài)內(nèi)部異常類
     */
    static class SecondCustomException extends Exception {

        public SecondCustomException() {
            super("第二個(gè)異常");
        }
    }

    /**
     * 第三個(gè)自定義的靜態(tài)內(nèi)部異常類
     */
    static class ThirdCustomException extends Exception {

        public ThirdCustomException() {
            super("第三個(gè)異常");
        }
    }

    /**
     * 測(cè)試異常鏈靜態(tài)方法1,直接拋出第一個(gè)自定義的靜態(tài)內(nèi)部異常類
     * @throws FirstCustomException
     */
    public static void f1() throws FirstCustomException {
        throw new FirstCustomException();
    }

    /**
     * 測(cè)試異常鏈靜態(tài)方法2,調(diào)用f1()方法,并拋出第二個(gè)自定義的靜態(tài)內(nèi)部異常類
     * @throws SecondCustomException
     */
    public static void f2() throws SecondCustomException {
        try {
            f1();
        } catch (FirstCustomException e) {
            throw new SecondCustomException();
        }
    }

    /**
     * 測(cè)試異常鏈靜態(tài)方法3,調(diào)用f2()方法, 并拋出第三個(gè)自定義的靜態(tài)內(nèi)部異常類
     * @throws ThirdCustomException
     */
    public static void f3() throws ThirdCustomException {
        try {
            f2();
        } catch (SecondCustomException e) {
            throw new ThirdCustomException();
        }
    }

    public static void main(String[] args) throws ThirdCustomException {
        // 調(diào)用靜態(tài)方法f3()
        f3();
    }
}

運(yùn)行結(jié)果:

Exception in thread "main" ExceptionDemo5$ThirdCustomException: 第三個(gè)異常
	at ExceptionDemo5.f3(ExceptionDemo5.java:46)
	at ExceptionDemo5.main(ExceptionDemo5.java:51)

運(yùn)行過(guò)程:

通過(guò)運(yùn)行結(jié)果,我們只獲取到了靜態(tài)方法 f3() 所拋出的異常堆棧信息,前面代碼所拋出的異常并沒(méi)有被顯示。

我們改寫上面的代碼,讓異常信息以鏈條的方式 “連接” 起來(lái)。可以通過(guò)改寫自定義異常的構(gòu)造方法,來(lái)獲取到之前異常的信息。實(shí)例如下:

/**
 * @author colorful@TaleLin
 */
public class ExceptionDemo6 {

    /**
     * 第一個(gè)自定義的靜態(tài)內(nèi)部異常類
     */
    static class FirstCustomException extends Exception {

        // 無(wú)參構(gòu)造方法
        public FirstCustomException() {
            super("第一個(gè)異常");
        }

    }

    /**
     * 第二個(gè)自定義的靜態(tài)內(nèi)部異常類
     */
    static class SecondCustomException extends Exception {

        /**
         * 通過(guò)構(gòu)造方法獲取之前異常的信息
         * @param cause 捕獲到的異常對(duì)象
         */
        public SecondCustomException(Throwable cause) {
            super("第二個(gè)異常", cause);
        }
    }

    /**
     * 第三個(gè)自定義的靜態(tài)內(nèi)部異常類
     */
    static class ThirdCustomException extends Exception {

        /**
         * 通過(guò)構(gòu)造方法獲取之前異常的信息
         * @param cause 捕獲到的異常對(duì)象
         */
        public ThirdCustomException(Throwable cause) {
            super("第三個(gè)異常", cause);
        }
    }

    /**
     * 測(cè)試異常鏈靜態(tài)方法1,直接拋出第一個(gè)自定義的靜態(tài)內(nèi)部異常類
     * @throws FirstCustomException
     */
    public static void f1() throws FirstCustomException {
        throw new FirstCustomException();
    }

    /**
     * 測(cè)試異常鏈靜態(tài)方法2,調(diào)用f1()方法,并拋出第二個(gè)自定義的靜態(tài)內(nèi)部異常類
     * @throws SecondCustomException
     */
    public static void f2() throws SecondCustomException {
        try {
            f1();
        } catch (FirstCustomException e) {
            throw new SecondCustomException(e);
        }
    }

    /**
     * 測(cè)試異常鏈靜態(tài)方法3,調(diào)用f2()方法, 并拋出第三個(gè)自定義的靜態(tài)內(nèi)部異常類
     * @throws ThirdCustomException
     */
    public static void f3() throws ThirdCustomException {
        try {
            f2();
        } catch (SecondCustomException e) {
            throw new ThirdCustomException(e);
        }
    }

    public static void main(String[] args) throws ThirdCustomException {
        // 調(diào)用靜態(tài)方法f3()
        f3();
    }
}

運(yùn)行結(jié)果:

Exception in thread "main" ExceptionDemo6$ThirdCustomException: 第三個(gè)異常
	at ExceptionDemo6.f3(ExceptionDemo6.java:74)
	at ExceptionDemo6.main(ExceptionDemo6.java:80)
Caused by: ExceptionDemo6$SecondCustomException: 第二個(gè)異常
	at ExceptionDemo6.f2(ExceptionDemo6.java:62)
	at ExceptionDemo6.f3(ExceptionDemo6.java:72)
	... 1 more
Caused by: ExceptionDemo6$FirstCustomException: 第一個(gè)異常
	at ExceptionDemo6.f1(ExceptionDemo6.java:51)
	at ExceptionDemo6.f2(ExceptionDemo6.java:60)
	... 2 more

運(yùn)行過(guò)程:

通過(guò)運(yùn)行結(jié)果,我們看到,異常發(fā)生的整個(gè)過(guò)程都打印到了屏幕上,這就是一個(gè)異常鏈。

8. 小結(jié)

通過(guò)本小節(jié)的學(xué)習(xí),我們知道了異常就是程序上的錯(cuò)誤,良好的異常處理可以提高代碼的健壯性。Java 語(yǔ)言中所有錯(cuò)誤(Error)和異常(Exception)的父類都是 Throwable。ErrorExceptionThrowable 的直接子類,我們通常說(shuō)的異常處理實(shí)際上就是處理 Exception 及其子類,異常又分為檢查型異常非檢查型異常。通過(guò)拋出異常和捕獲異常來(lái)實(shí)現(xiàn)異常處理。我們亦可以通過(guò)繼承 Throwable 類或者它的子類來(lái)自定義異常類。通過(guò)構(gòu)造方法獲取之前異常的信息可以實(shí)現(xiàn)異常鏈。