JAVA基礎(chǔ)之異常

1棱诱、概述

當(dāng)方法內(nèi)部發(fā)生一項(xiàng)錯(cuò)誤時(shí)藻雪,該方法會(huì)創(chuàng)建一個(gè)對(duì)象傳遞給運(yùn)行時(shí)系統(tǒng)(runtime system),這個(gè)對(duì)象被稱為異常對(duì)象魁索,包含錯(cuò)誤的類型、發(fā)生位置盼铁,程序狀態(tài)等一系列信息粗蔚。

當(dāng)一個(gè)方法拋出異常時(shí),運(yùn)行時(shí)系統(tǒng)會(huì)沿著調(diào)用棧尋找該異常的處理方式 饶火。

下圖中鹏控,調(diào)用棧下面的方法調(diào)用了上面的方法,層層嵌套肤寝,一共四層:

1.png

調(diào)用第三個(gè)方法時(shí)拋出了一個(gè)異常当辐,運(yùn)行時(shí)系統(tǒng)就會(huì)沿著調(diào)用棧反向?qū)ふ以摦惓5奶幚沓绦颍?dāng)該異常類型與某個(gè)異常處理程序聲明的異常類型一致時(shí)鲤看,系統(tǒng)就將該異常交給它處理缘揪。

2.png

如果系統(tǒng)沒能找到合適的異常處理程序,系統(tǒng)將會(huì)終止义桂。

2找筝、異常類型

java提供了兩種處理異常的方式:

  • 使用try語句捕獲異常并處理;
  • 使用throws關(guān)鍵字列出要拋出的異常類型慷吊,代表在本方法內(nèi)不做處理袖裕,但是調(diào)用該方法的方法必須處理該異常或者繼續(xù)拋出溉瓶。

并不是所有異常都需要顯式處理(這里的處理代表在程序內(nèi)部捕獲或者拋出)急鳄,比如IOException、SQLException等是必須要處理的堰酿,而NullPointerException疾宏、ArithmeticException、IndexOutOfBoundsException等可以不作處理胞锰。

理解這一點(diǎn)灾锯,就要弄清異常的基本分類。

2.1 Checked Exception

這類異常是應(yīng)用程序可以預(yù)見并能夠恢復(fù)的錯(cuò)誤嗅榕,比如顺饮,應(yīng)用程序需要用戶輸入一個(gè)文件名,然后程序?qū)?duì)這個(gè)文件進(jìn)行讀寫操作凌那。假如用戶輸入的文件名不存在兼雄,拋出java.io.FileNotFoundException,應(yīng)用程序應(yīng)該捕獲這個(gè)異常并提醒用戶。類似這種異常就屬于checked exception也榄。

除了Error、RuntimeException以及兩者的子類南蹂,所有異常都屬于checked exception佃乘。

Error與runtime exception合稱為unchecked exception囱井。

2.2 Error

Error一般來說是應(yīng)用程序外部引起的異常,應(yīng)用程序通常不能預(yù)見并恢復(fù)趣避。比如庞呕,程序順利打開了一個(gè)文件,但是由于硬件或者操作系統(tǒng)故障程帕,不能夠讀取文件中的內(nèi)容住练,程序就會(huì)拋出java.io.IOError

Error類型的異常不是必須處理的異常愁拭,當(dāng)然讲逛,你也可以選擇處理它。

2.3 Runtime Exception

runtime exception一般來說是程序內(nèi)部引起的異常岭埠,應(yīng)用程序通常能夠預(yù)見并恢復(fù)盏混。這類異常的出現(xiàn)一般暗示程序存在bug。比如惜论,還是文件操作的例子括饶,由于邏輯錯(cuò)誤,傳入的文件名為空值来涨,程序就會(huì)拋出一個(gè)NullPointerException图焰。

雖然可以讓程序捕獲runtime exception,但更合適的做法是剔除引起這類異常的bug蹦掐。

java的異常類層次圖如下:

3.png

3技羔、 異常的捕獲

checked exception必須捕獲,而unchecked exception的捕獲不是必須的卧抗。例如:

import java.io .*;
import java.util.List;
import java.util.ArrayList;

public class ListOfNumbers {
    private List<Integer> list;
    private static final int SIZE = 10;

    public ListOfNumbers() {
        list = new ArrayList<Integer>(SIZE);
        for (int i = 0; i < SIZE; i++) {
            list.add(new Integer(i));
        }
    }

    public void writeList() {
        try {
            // FileWriter的構(gòu)造方法 throws IOException, checked exception類型藤滥,必須捕獲
            PrintWriter out = new PrintWriter(new FileWriter("OutFile.txt"));

            for (int i = 0; i < SIZE; i++) {
             // get(int)方法throws IndexOutOfBoundsException,RuntimeException的子類社裆,unchecked exception類型拙绊,不是必須要捕獲的
                out.println("Value at: " + i + " = " + list.get(i));
            }
            out.close();
        } catch (IOException e) {  //捕獲IOException
            //...
        }
    }
}

unchecked exception在一些特殊的情況下也可以選擇捕獲它,比如上面的程序泳秀,現(xiàn)在既要捕獲IOException标沪,也要捕獲IndexOutOfBoundsException,改寫如下:

    public void writeList() {
        try {
            // FileWriter的構(gòu)造方法 throws IOException, checked exception類型嗜傅,必須捕獲
            PrintWriter out = new PrintWriter(new FileWriter("OutFile.txt"));

            for (int i = 0; i < SIZE; i++) {
            // get(int)方法throws IndexOutOfBoundsException金句,RuntimeException的子類,unchecked exception類型吕嘀,不是必須要捕獲的
                out.println("Value at: " + i + " = " + list.get(i));
            }
            out.close();
        } catch (IndexOutOfBoundsException e) {  //捕獲IndexOutOfBoundsException
            //...
        } catch (IOException e) {  //捕獲IOException
            //...
        }

    }

如果要同時(shí)捕獲的異常存在繼承關(guān)系违寞,即某個(gè)異常時(shí)另一個(gè)異常的子類贞瞒,必須把父類異常寫在子類異常后面,否則會(huì)報(bào)編譯錯(cuò)誤趁曼。但是军浆,無論有幾個(gè)捕獲語句,最終至多會(huì)進(jìn)入一個(gè)catch語句挡闰。

例如:

    public class Example {

        public void test() throws IOException {
            throw new IOException();
        }

        public static void main(String args[]) {

            Example example = new Example();

            try {
                example.test();
            } catch (IOException e) {
                System.out.println("捕獲了子類異常");
            } catch (Exception e) {
                System.out.println("捕獲了父類異常");
            }

        }

    }

上例中瘾敢,IOException是Exception的子類,如果方法拋出了IOException異常尿这,會(huì)進(jìn)入第一個(gè)catch子句,但不會(huì)進(jìn)入第二個(gè)catch語句庆杜;如果拋出的是非IOException的其他Exception子類異常射众,則會(huì)直接進(jìn)入第二個(gè)catch子句。也就是說晃财,不會(huì)同時(shí)進(jìn)入兩個(gè)catch子句叨橱。

從Java SE 7以后,一個(gè)catch塊可以捕獲多個(gè)異常断盛,上面的捕獲語句可簡寫為:

catch (IndexOutOfBoundsException | IOException ex) {
    ...
}

需要注意的是罗洗,這種情況下,catch的參數(shù)(上例中的“ex”)默認(rèn)是final的钢猛,不能夠在catch塊中對(duì)它再次賦值伙菜。

無論異常是否發(fā)生,try代碼塊退出后命迈,finally代碼塊都會(huì)執(zhí)行贩绕,常常用于釋放資源。例如:

    public void writeList() {
        PrintWriter out = null;
        try {
            out = new PrintWriter(new FileWriter("OutFile.txt"));

            for (int i = 0; i < SIZE; i++) {
                out.println("Value at: " + i + " = " + list.get(i));
            }

        } catch (IOException e) {  
        ...
        } finally {  //釋放資源
            if (out != null) {
                out.close();
            }
        }
    }

有三種情況可導(dǎo)致try代碼塊退出:

  • new FileWriter("OutFile.txt")拋出IOException
  • list.get(i)拋出IndexOutOfBoundsException
  • 無異常拋出壶愤,代碼執(zhí)行完畢

無論發(fā)生了上面的那種情況淑倾,運(yùn)行時(shí)系統(tǒng)都會(huì)保證finally代碼塊中的程序執(zhí)行。

需要注意的是征椒,如果在執(zhí)行try-catch代碼塊的時(shí)候JVM退出了娇哆,或者執(zhí)行try-catch代碼塊的線程被中斷或者殺死,或者使用了System.exit()函數(shù)等勃救,finally代碼塊有可能不被執(zhí)行碍讨。

帶有返回值的函數(shù)中使用了try-catch-finally塊,且返回值與是否發(fā)生異常有關(guān)蒙秒,則應(yīng)該避免將返回值寫在finally塊中垄开,因?yàn)闊o論是否會(huì)發(fā)生異常,都會(huì)按照finally塊的返回值税肪,而忽略try-catch任何地方的返回值溉躲。例如:

   public int test() {
        InputStream in = null;
        try {
            File f = new File("F:\test.txt");
            in = new FileInputStream(f);
            return 1;
        } catch (IOException e) {
            e.printStackTrace();
            return 2;
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return 3;
        }
    }

上例中榜田,無論是否會(huì)拋出IOException,都不會(huì)返回1或2锻梳,只會(huì)返回3箭券。

在try中聲明的一個(gè)或多個(gè)資源,在程序結(jié)束后應(yīng)該關(guān)閉疑枯,通常我們是在finally代碼塊中完成這項(xiàng)工作辩块。

從java 7開始,提供了一種更為簡潔有效的寫法: try-with-resources語句荆永。

使用形式如下:

   static String readFirstLineFromFile(String path) throws IOException {
        try (BufferedReader br =
                     new BufferedReader(new FileReader(path))) {
            return br.readLine();
        }
    }

try-with-resources確保{}內(nèi)的程序執(zhí)行完畢后自動(dòng)關(guān)閉資源废亭,所有實(shí)現(xiàn)了java.lang.AutoCloseable接口(java 7新增的接口,java.lang.Closeable的父接口具钥,)的對(duì)象都可以當(dāng)作資源豆村。

4、throw與throws

捕獲異常的前提是有方法拋出了異常骂删。

throw關(guān)鍵字用于在方法體內(nèi)部掌动,發(fā)生錯(cuò)誤的地方拋出異常。如:

    public Object pop() {
        Object obj;

        if (size == 0) {  //棧為空宁玫,拋出異常
            throw new EmptyStackException();
        }

        obj = objectAt(size - 1);
        setObjectAt(size - 1, null);
        size--;
        return obj;
    }

該方法用于從棧中彈出棧頂元素粗恢,但是,如果棧為空就不能進(jìn)行這項(xiàng)操作欧瘪,所以就會(huì)拋出EmptyStackException異常眷射。

當(dāng)其他方法調(diào)用pop()方法時(shí),就應(yīng)該考慮到pop()可能會(huì)拋出的異常佛掖,如果如果pop()拋出的Unchecked Exception凭迹,可以不做額外的處理;如果pop()拋出的是checked Exception則必須進(jìn)行處理苦囱,可以用try-catch捕獲嗅绸,也可以選擇在本方法內(nèi)不做捕獲,繼續(xù)用throws關(guān)鍵字拋出撕彤,如:

public void callPop() throws EmptyStackException {
    //...
    pop();  //該方法可能會(huì)拋出EmptyStackException
    //...
}

實(shí)際上鱼鸠,一個(gè)異常很多時(shí)候是由于另一個(gè)cause異常引起的,因?yàn)?cause 自身也會(huì)有 cause羹铅,依此類推蚀狰,就形成了鏈?zhǔn)疆惓?Chained Exceptions)。例如:

try {
    //...
} catch (IOException e) {  //捕獲到IOException時(shí)职员,拋出另一個(gè)異常
    throw new SampleException("Other IOException", e);
}

Throwable有一種構(gòu)造函數(shù)可以接受Throwable類型的參數(shù)作為引起該異常的cause麻蹋,Throwable類的initCause(Throwable)、getCause()可以設(shè)置cause信息焊切、獲取cause信息扮授。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末芳室,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子刹勃,更是在濱河造成了極大的恐慌堪侯,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件荔仁,死亡現(xiàn)場離奇詭異伍宦,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)乏梁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門次洼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人遇骑,你說我怎么就攤上這事卖毁。” “怎么了质蕉?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長翩肌。 經(jīng)常有香客問我模暗,道長,這世上最難降的妖魔是什么念祭? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任兑宇,我火速辦了婚禮,結(jié)果婚禮上粱坤,老公的妹妹穿的比我還像新娘隶糕。我一直安慰自己,他們只是感情好站玄,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布枚驻。 她就那樣靜靜地躺著,像睡著了一般株旷。 火紅的嫁衣襯著肌膚如雪再登。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天晾剖,我揣著相機(jī)與錄音锉矢,去河邊找鬼。 笑死齿尽,一個(gè)胖子當(dāng)著我的面吹牛沽损,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播循头,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼绵估,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼炎疆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起壹士,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤磷雇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后躏救,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體唯笙,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年盒使,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了崩掘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡少办,死狀恐怖苞慢,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情英妓,我是刑警寧澤挽放,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站蔓纠,受9級(jí)特大地震影響辑畦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜腿倚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一纯出、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧敷燎,春花似錦暂筝、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至饭豹,卻和暖如春胧洒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背墨状。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國打工卫漫, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人肾砂。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓列赎,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子包吝,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 什么是異常饼煞? ?異常是在程序運(yùn)行過程中發(fā)生的,會(huì)打斷程序正常執(zhí)行的事件诗越。異匙┣疲可以分為兩大類Error和Except...
    小任務(wù)大夢(mèng)想閱讀 464評(píng)論 0 1
  • 1. java的異常體系包含在java.lang這個(gè)包默認(rèn)不需要導(dǎo)入。 2. java異常體系 |——Throw...
    沈默的頭號(hào)狗腿閱讀 195評(píng)論 0 0
  • 導(dǎo)語 異常指的是在程序運(yùn)行過程中發(fā)生的異常事件嚷狞,通常是由硬件問題或者程序設(shè)計(jì)問題所導(dǎo)致的块促。異常處理提供了處理程序運(yùn)...
    jerrwy閱讀 493評(píng)論 0 2
  • 異常:由于操作不規(guī)范、輸入不符合要求床未、資源找不到等異于平常的情況竭翠,會(huì)導(dǎo)致程序出錯(cuò)。導(dǎo)致程序不正常運(yùn)行薇搁。異常類型最高...
    從小白到全棧閱讀 238評(píng)論 0 0
  • 1. Java異常體系 ThrowableThrowable是整個(gè)異常體系的頂層父類斋扰,它有兩個(gè)子類,分別是Erro...
    sofarsogoo_932d閱讀 501評(píng)論 0 0