Java中的異常和處理詳解

Java中的異常和處理詳解

簡介

程序運(yùn)行時(shí)菱鸥,發(fā)生的不被期望的事件宗兼,它阻止了程序按照程序員的預(yù)期正常執(zhí)行,這就是異常氮采。異常發(fā)生時(shí)殷绍,是任程序自生自滅,立刻退出終止鹊漠,還是輸出錯(cuò)誤給用戶主到?或者用C語言風(fēng)格:用函數(shù)返回值作為執(zhí)行狀態(tài)?躯概。

Java提供了更加優(yōu)秀的解決辦法:異常處理機(jī)制登钥。

異常處理機(jī)制能讓程序在異常發(fā)生時(shí),按照代碼的預(yù)先設(shè)定的異常處理邏輯娶靡,針對(duì)性地處理異常牧牢,讓程序盡最大可能恢復(fù)正常并繼續(xù)執(zhí)行,且保持代碼的清晰。

Java中的異乘ⅲ可以是函數(shù)中的語句執(zhí)行時(shí)引發(fā)的度陆,也可以是程序員通過throw 語句手動(dòng)拋出的,只要在Java程序中產(chǎn)生了異常献幔,就會(huì)用一個(gè)對(duì)應(yīng)類型的異常對(duì)象來封裝異常懂傀,JRE就會(huì)試圖尋找異常處理程序來處理異常。

Throwable類是Java異常類型的頂層父類蜡感,一個(gè)對(duì)象只有是 Throwable 類的(直接或者間接)實(shí)例蹬蚁,他才是一個(gè)異常對(duì)象,才能被異常處理機(jī)制識(shí)別郑兴。JDK中內(nèi)建了一些常用的異常類犀斋,我們也可以自定義異常。

Java異常的分類和類結(jié)構(gòu)圖

Java標(biāo)準(zhǔn)褲內(nèi)建了一些通用的異常情连,這些類以Throwable為頂層父類叽粹。

Throwable又派生出Error類和Exception類斑芜。

錯(cuò)誤:Error類以及他的子類的實(shí)例所袁,代表了JVM本身的錯(cuò)誤枚尼。錯(cuò)誤不能被程序員通過代碼處理甜紫,Error很少出現(xiàn)叛氨。因此矛纹,程序員應(yīng)該關(guān)注Exception為父類的分支下的各種異常類话原。

異常:Exception以及他的子類蕾管,代表程序運(yùn)行時(shí)發(fā)送的各種不期望發(fā)生的事件螃诅》惹猓可以被Java異常處理機(jī)制使用,是異常處理的核心术裸。

image

總體上我們根據(jù)Javac對(duì)異常的處理要求倘是,將異常類分為2類。

非檢查異常(unckecked exception):Error 和 RuntimeException 以及他們的子類袭艺。javac在編譯時(shí)搀崭,不會(huì)提示和發(fā)現(xiàn)這樣的異常,不要求在程序處理這些異常匹表。所以如果愿意门坷,我們可以編寫代碼處理(使用try...catch...finally)這樣的異常,也可以不處理袍镀。對(duì)于這些異常默蚌,我們應(yīng)該修正代碼,而不是去通過異常處理器處理 苇羡。這樣的異常發(fā)生的原因多半是代碼寫的有問題绸吸。如除0錯(cuò)誤ArithmeticException,錯(cuò)誤的強(qiáng)制類型轉(zhuǎn)換錯(cuò)誤ClassCastException,數(shù)組索引越界ArrayIndexOutOfBoundsException锦茁,使用了空對(duì)象NullPointerException等等攘轩。

檢查異常(checked exception):除了Error 和 RuntimeException的其它異常。javac強(qiáng)制要求程序員為這樣的異常做預(yù)備處理工作(使用try...catch...finally或者throws)码俩。在方法中要么用try-catch語句捕獲它并處理度帮,要么用throws子句聲明拋出它,否則編譯不會(huì)通過稿存。這樣的異常一般是由程序的運(yùn)行環(huán)境導(dǎo)致的笨篷。因?yàn)槌绦蚩赡鼙贿\(yùn)行在各種未知的環(huán)境下,而程序員無法干預(yù)用戶如何使用他編寫的程序瓣履,于是程序員就應(yīng)該為這樣的異常時(shí)刻準(zhǔn)備著率翅。如SQLException , IOException,ClassNotFoundException 等。

需要明確的是:檢查和非檢查是對(duì)于javac來說的袖迎,這樣就很好理解和區(qū)分了冕臭。

初識(shí)異常

下面的代碼會(huì)演示2個(gè)異常類型:ArithmeticException 和 InputMismatchException。前者由于整數(shù)除0引發(fā)燕锥,后者是輸入的數(shù)據(jù)不能被轉(zhuǎn)換為int類型引發(fā)辜贵。

package com.example;
import java. util .Scanner ;
public class AllDemo
{
      public static void main (String [] args )
      {
            System . out. println( "----歡迎使用命令行除法計(jì)算器----" ) ;
            CMDCalculate ();
      }
      public static void CMDCalculate ()
      {
            Scanner scan = new Scanner ( System. in );
            int num1 = scan .nextInt () ;
            int num2 = scan .nextInt () ;
            int result = devide (num1 , num2 ) ;
            System . out. println( "result:" + result) ;
            scan .close () ;
      }
      public static int devide (int num1, int num2 ){
            return num1 / num2 ;
      }
}
/*****************************************

----歡迎使用命令行除法計(jì)算器----
0
Exception in thread "main" java.lang.ArithmeticException : / by zero
     at com.example.AllDemo.devide( AllDemo.java:30 )
     at com.example.AllDemo.CMDCalculate( AllDemo.java:22 )
     at com.example.AllDemo.main( AllDemo.java:12 )

----歡迎使用命令行除法計(jì)算器----
r
Exception in thread "main" java.util.InputMismatchException
     at java.util.Scanner.throwFor( Scanner.java:864 )
     at java.util.Scanner.next( Scanner.java:1485 )
     at java.util.Scanner.nextInt( Scanner.java:2117 )
     at java.util.Scanner.nextInt( Scanner.java:2076 )
     at com.example.AllDemo.CMDCalculate( AllDemo.java:20 )
     at com.example.AllDemo.main( AllDemo.java:12 )
*****************************************/

異常是在執(zhí)行某個(gè)函數(shù)時(shí)引發(fā)的,而函數(shù)又是層級(jí)調(diào)用脯宿,形成調(diào)用棧的念颈,因?yàn)椋灰粋€(gè)函數(shù)發(fā)生了異常连霉,那么他的所有的caller都會(huì)被異常影響。當(dāng)這些被影響的函數(shù)以異常信息輸出時(shí)嗡靡,就形成的了異常追蹤棧跺撼。

異常最先發(fā)生的地方,叫做異常拋出點(diǎn)讨彼。

image

從上面的例子可以看出歉井,當(dāng)devide函數(shù)發(fā)生除0異常時(shí),devide函數(shù)將拋出ArithmeticException異常哈误,因此調(diào)用他的CMDCalculate函數(shù)也無法正常完成哩至,因此也發(fā)送異常,而CMDCalculate的caller——main 因?yàn)镃MDCalculate拋出異常蜜自,也發(fā)生了異常菩貌,這樣一直向調(diào)用棧的棧底回溯。這種行為叫做異常的冒泡重荠,異常的冒泡是為了在當(dāng)前發(fā)生異常的函數(shù)或者這個(gè)函數(shù)的caller中找到最近的異常處理程序箭阶。由于這個(gè)例子中沒有使用任何異常處理機(jī)制,因此異常最終由main函數(shù)拋給JRE,導(dǎo)致程序終止仇参。

上面的代碼不使用異常處理機(jī)制嘹叫,也可以順利編譯,因?yàn)?個(gè)異常都是非檢查異常诈乒。但是下面的例子就必須使用異常處理機(jī)制罩扇,因?yàn)楫惓J菣z查異常。

代碼中我選擇使用throws聲明異常怕磨,讓函數(shù)的調(diào)用者去處理可能發(fā)生的異常暮蹂。但是為什么只throws了IOException呢?因?yàn)镕ileNotFoundException是IOException的子類癌压,在處理范圍內(nèi)仰泻。

@Test
public void testException() throws IOException
{
    //FileInputStream的構(gòu)造函數(shù)會(huì)拋出FileNotFoundException
    FileInputStream fileIn = new FileInputStream("E:\\a.txt");
    
    int word;
    //read方法會(huì)拋出IOException
    while((word =  fileIn.read())!=-1) 
    {
        System.out.print((char)word);
    }
    //close方法會(huì)拋出IOException
    fileIn.clos
}

異常處理的基本語法

在編寫代碼處理異常時(shí),對(duì)于檢查異常滩届,有2種不同的處理方式:使用try...catch...finally語句塊處理它集侯。或者帜消,在函數(shù)簽名中使用throws 聲明交給函數(shù)調(diào)用者caller去解決棠枉。

try...catch...finally語句塊

try{
     //try塊中放可能發(fā)生異常的代碼。
     //如果執(zhí)行完try且不發(fā)生異常泡挺,則接著去執(zhí)行finally塊和finally后面的代碼(如果有的話)辈讶。
     //如果發(fā)生異常,則嘗試去匹配catch塊娄猫。

}catch(SQLException SQLexception){
    //每一個(gè)catch塊用于捕獲并處理一個(gè)特定的異常贱除,或者這異常類型的子類。Java7中可以將多個(gè)異常聲明在一個(gè)catch中媳溺。
    //catch后面的括號(hào)定義了異常類型和異常參數(shù)月幌。如果異常與之匹配且是最先匹配到的,則虛擬機(jī)將使用這個(gè)catch塊來處理異常悬蔽。
    //在catch塊中可以使用這個(gè)塊的異常參數(shù)來獲取異常的相關(guān)信息扯躺。異常參數(shù)是這個(gè)catch塊中的局部變量,其它塊不能訪問蝎困。
    //如果當(dāng)前try塊中發(fā)生的異常在后續(xù)的所有catch中都沒捕獲到录语,則先去執(zhí)行finally,然后到這個(gè)函數(shù)的外部caller中去匹配異常處理器禾乘。
    //如果try中沒有發(fā)生異常澎埠,則所有的catch塊將被忽略。

}catch(Exception exception){
    //...
}finally{
   
    //finally塊通常是可選的盖袭。
   //無論異常是否發(fā)生失暂,異常是否匹配被處理彼宠,finally都會(huì)執(zhí)行。
   //一個(gè)try至少要有一個(gè)catch塊弟塞,否則凭峡, 至少要有1個(gè)finally塊。但是finally不是用來處理異常的决记,finally不會(huì)捕獲異常摧冀。
  //finally主要做一些清理工作,如流的關(guān)閉系宫,數(shù)據(jù)庫連接的關(guān)閉等索昂。 
}

需要注意的地方

1、try塊中的局部變量和catch塊中的局部變量(包括異常變量)扩借,以及finally中的局部變量椒惨,他們之間不可共享使用。

2潮罪、每一個(gè)catch塊用于處理一個(gè)異常康谆。異常匹配是按照catch塊的順序從上往下尋找的,只有第一個(gè)匹配的catch會(huì)得到執(zhí)行嫉到。匹配時(shí)沃暗,不僅運(yùn)行精確匹配,也支持父類匹配何恶,因此孽锥,如果同一個(gè)try塊下的多個(gè)catch異常類型有父子關(guān)系,應(yīng)該將子類異常放在前面细层,父類異常放在后面惜辑,這樣保證每個(gè)catch塊都有存在的意義。

3今艺、java中韵丑,異常處理的任務(wù)就是將執(zhí)行控制流從異常發(fā)生的地方轉(zhuǎn)移到能夠處理這種異常的地方去。也就是說:當(dāng)一個(gè)函數(shù)的某條語句發(fā)生異常時(shí)虚缎,這條語句的后面的語句不會(huì)再執(zhí)行,它失去了焦點(diǎn)钓株。執(zhí)行流跳轉(zhuǎn)到最近的匹配的異常處理catch代碼塊去執(zhí)行实牡,異常被處理完后,執(zhí)行流會(huì)接著在“處理了這個(gè)異常的catch代碼塊”后面接著執(zhí)行轴合。

有的編程語言當(dāng)異常被處理后创坞,控制流會(huì)恢復(fù)到異常拋出點(diǎn)接著執(zhí)行,這種策略叫做:resumption model of exception handling(恢復(fù)式異常處理模式 )

而Java則是讓執(zhí)行流恢復(fù)到處理了異常的catch塊后接著執(zhí)行受葛,這種策略叫做:termination model of exception handling(終結(jié)式異常處理模式)

public static void main(String[] args){
        try {
            foo();
        }catch(ArithmeticException ae) {
            System.out.println("處理異常");
        }
}
public static void foo(){
        int a = 5/0;  //異常拋出點(diǎn)
        System.out.println("為什么還不給我漲工資!!!");  //////////////////////不會(huì)執(zhí)行
}

throws 函數(shù)聲明

throws聲明:如果一個(gè)方法內(nèi)部的代碼會(huì)拋出檢查異常(checked exception)题涨,而方法自己又沒有完全處理掉偎谁,則javac保證你必須在方法的簽名上使用throws關(guān)鍵字聲明這些可能拋出的異常,否則編譯不通過纲堵。

throws是另一種處理異常的方式巡雨,它不同于try...catch...finally,throws僅僅是將函數(shù)中可能出現(xiàn)的異常向調(diào)用者聲明席函,而自己則不具體處理铐望。

采取這種異常處理的原因可能是:方法本身不知道如何處理這樣的異常,或者說讓調(diào)用者處理更好茂附,調(diào)用者需要為可能發(fā)生的異常負(fù)責(zé)正蛙。

public void foo() throws ExceptionType1 , ExceptionType2 ,ExceptionTypeN
{ 
     //foo內(nèi)部可以拋出 ExceptionType1 , ExceptionType2 ,ExceptionTypeN 類的異常,或者他們的子類的異常對(duì)象营曼。
}

finally塊

finally塊不管異常是否發(fā)生乒验,只要對(duì)應(yīng)的try執(zhí)行了,則它一定也執(zhí)行蒂阱。只有一種方法讓finally塊不執(zhí)行:System.exit()锻全。因此finally塊通常用來做資源釋放操作:關(guān)閉文件,關(guān)閉數(shù)據(jù)庫連接等等蒜危。

良好的編程習(xí)慣是:在try塊中打開資源虱痕,在finally塊中清理釋放這些資源。

需要注意的地方:

1辐赞、finally塊沒有處理異常的能力部翘。處理異常的只能是catch塊。

2响委、在同一try...catch...finally塊中 新思,如果try中拋出異常,且有匹配的catch塊赘风,則先執(zhí)行catch塊夹囚,再執(zhí)行finally塊。如果沒有catch塊匹配邀窃,則先執(zhí)行finally荸哟,然后去外面的調(diào)用者中尋找合適的catch塊。

3瞬捕、在同一try...catch...finally塊中 鞍历,try發(fā)生異常,且匹配的catch塊中處理異常時(shí)也拋出異常肪虎,那么后面的finally也會(huì)執(zhí)行:首先執(zhí)行finally塊劣砍,然后去外圍調(diào)用者中尋找合適的catch塊。

這是正常的情況扇救,但是也有特例刑枝。關(guān)于finally有很多惡心香嗓,偏、怪装畅、難的問題靠娱,我在本文最后統(tǒng)一介紹了,電梯速達(dá)->:finally塊和return

throw 異常拋出語句

throw exceptionObject

程序員也可以通過throw語句手動(dòng)顯式的拋出一個(gè)異常洁灵。throw語句的后面必須是一個(gè)異常對(duì)象饱岸。

throw 語句必須寫在函數(shù)中,執(zhí)行throw 語句的地方就是一個(gè)異常拋出點(diǎn)徽千,它和由JRE自動(dòng)形成的異常拋出點(diǎn)沒有任何差別苫费。

public void save(User user)
{
      if(user  == null) 
          throw new IllegalArgumentException("User對(duì)象為空");
      //......
        
}

異常的鏈化

在一些大型的,模塊化的軟件開發(fā)中双抽,一旦一個(gè)地方發(fā)生異常百框,則如骨牌效應(yīng)一樣,將導(dǎo)致一連串的異常牍汹。假設(shè)B模塊完成自己的邏輯需要調(diào)用A模塊的方法铐维,如果A模塊發(fā)生異常,則B也將不能完成而發(fā)生異常慎菲,但是B在拋出異常時(shí)嫁蛇,會(huì)將A的異常信息掩蓋掉,這將使得異常的根源信息丟失露该。異常的鏈化可以將多個(gè)模塊的異常串聯(lián)起來睬棚,使得異常信息不會(huì)丟失。

異常鏈化:以一個(gè)異常對(duì)象為參數(shù)構(gòu)造新的異常對(duì)象解幼。新的異對(duì)象將包含先前異常的信息抑党。這項(xiàng)技術(shù)主要是異常類的一個(gè)帶Throwable參數(shù)的函數(shù)來實(shí)現(xiàn)的。這個(gè)當(dāng)做參數(shù)的異常撵摆,我們叫他根源異常(cause)底靠。

查看Throwable類源碼,可以發(fā)現(xiàn)里面有一個(gè)Throwable字段cause特铝,就是它保存了構(gòu)造時(shí)傳遞的根源異常參數(shù)暑中。這種設(shè)計(jì)和鏈表的結(jié)點(diǎn)類設(shè)計(jì)如出一轍,因此形成鏈也是自然的了鲫剿。

public class Throwable implements Serializable {
    private Throwable cause = this;
   
    public Throwable(String message, Throwable cause) {
        fillInStackTrace();
        detailMessage = message;
        this.cause = cause;
    }
     public Throwable(Throwable cause) {
        fillInStackTrace();
        detailMessage = (cause==null ? null : cause.toString());
        this.cause = cause;
    }
    
    //........
} 

下面是一個(gè)例子痒芝,演示了異常的鏈化:從命令行輸入2個(gè)int,將他們相加牵素,輸出。輸入的數(shù)不是int澄者,則導(dǎo)致getInputNumbers異常笆呆,從而導(dǎo)致add函數(shù)異常请琳,則可以在add函數(shù)中拋出

一個(gè)鏈化的異常。

public static void main(String[] args)
{
    
    System.out.println("請(qǐng)輸入2個(gè)加數(shù)");
    int result;
    try
    {
        result = add();
        System.out.println("結(jié)果:"+result);
    } catch (Exception e){
        e.printStackTrace();
    }
}
//獲取輸入的2個(gè)整數(shù)返回
private static List<Integer> getInputNumbers()
{
    List<Integer> nums = new ArrayList<>();
    Scanner scan = new Scanner(System.in);
    try {
        int num1 = scan.nextInt();
        int num2 = scan.nextInt();
        nums.add(new Integer(num1));
        nums.add(new Integer(num2));
    }catch(InputMismatchException immExp){
        throw immExp;
    }finally {
        scan.close();
    }
    return nums;
}

//執(zhí)行加法計(jì)算
private static int add() throws Exception
{
    int result;
    try {
        List<Integer> nums =getInputNumbers();
        result = nums.get(0)  + nums.get(1);
    }catch(InputMismatchException immExp){
        throw new Exception("計(jì)算失敗",immExp);  /////////////////////////////鏈化:以一個(gè)異常對(duì)象為參數(shù)構(gòu)造新的異常對(duì)象赠幕。
    }
    return  result;
}

/*
請(qǐng)輸入2個(gè)加數(shù)
r 1
java.lang.Exception: 計(jì)算失敗
    at practise.ExceptionTest.add(ExceptionTest.java:53)
    at practise.ExceptionTest.main(ExceptionTest.java:18)
Caused by: java.util.InputMismatchException
    at java.util.Scanner.throwFor(Scanner.java:864)
    at java.util.Scanner.next(Scanner.java:1485)
    at java.util.Scanner.nextInt(Scanner.java:2117)
    at java.util.Scanner.nextInt(Scanner.java:2076)
    at practise.ExceptionTest.getInputNumbers(ExceptionTest.java:30)
    at practise.ExceptionTest.add(ExceptionTest.java:48)
    ... 1 more

*/
image

自定義異常

如果要自定義異常類俄精,則擴(kuò)展Exception類即可,因此這樣的自定義異常都屬于檢查異常(checked exception)榕堰。如果要自定義非檢查異常竖慧,則擴(kuò)展自RuntimeException。

按照國際慣例逆屡,自定義的異常應(yīng)該總是包含如下的構(gòu)造函數(shù):

  • 一個(gè)無參構(gòu)造函數(shù)
  • 一個(gè)帶有String參數(shù)的構(gòu)造函數(shù)圾旨,并傳遞給父類的構(gòu)造函數(shù)。
  • 一個(gè)帶有String參數(shù)和Throwable參數(shù)魏蔗,并都傳遞給父類構(gòu)造函數(shù)
  • 一個(gè)帶有Throwable 參數(shù)的構(gòu)造函數(shù)砍的,并傳遞給父類的構(gòu)造函數(shù)。

下面是IOException類的完整源代碼莺治,可以借鑒廓鞠。

public class IOException extends Exception
{
    static final long serialVersionUID = 7818375828146090155L;

    public IOException()
    {
        super();
    }

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

    public IOException(String message, Throwable cause)
    {
        super(message, cause);
    }

    
    public IOException(Throwable cause)
    {
        super(cause);
    }
}

異常的注意事項(xiàng)

1、當(dāng)子類重寫父類的帶有 throws聲明的函數(shù)時(shí)谣旁,其throws聲明的異常必須在父類異常的可控范圍內(nèi)——用于處理父類的throws方法的異常處理器床佳,必須也適用于子類的這個(gè)帶throws方法 。這是為了支持多態(tài)榄审。

例如砌们,父類方法throws 的是2個(gè)異常,子類就不能throws 3個(gè)及以上的異常瘟判。父類throws IOException怨绣,子類就必須throws IOException或者IOException的子類。

至于為什么拷获?我想篮撑,也許下面的例子可以說明。

class Father
{
    public void start() throws IOException
    {
        throw new IOException();
    }
}

class Son extends Father
{
    public void start() throws Exception
    {
        throw new SQLException();
    }
}
/**********************假設(shè)上面的代碼是允許的(實(shí)質(zhì)是錯(cuò)誤的)***********************/
class Test
{
    public static void main(String[] args)
    {
        Father[] objs = new Father[2];
        objs[0] = new Father();
        objs[1] = new Son();

        for(Father obj:objs)
        {
        //因?yàn)镾on類拋出的實(shí)質(zhì)是SQLException匆瓜,而IOException無法處理它赢笨。
        //那么這里的try。驮吱。catch就不能處理Son中的異常茧妒。
        //多態(tài)就不能實(shí)現(xiàn)了。
            try {
                 obj.start();
            }catch(IOException)
            {
                 //處理IOException
            }
         }
   }
}

2左冬、Java程序可以是多線程的桐筏。每一個(gè)線程都是一個(gè)獨(dú)立的執(zhí)行流,獨(dú)立的函數(shù)調(diào)用棧拇砰。如果程序只有一個(gè)線程梅忌,那么沒有被任何代碼處理的異常 會(huì)導(dǎo)致程序終止狰腌。如果是多線程的,那么沒有被任何代碼處理的異常僅僅會(huì)導(dǎo)致異常所在的線程結(jié)束牧氮。

也就是說琼腔,Java中的異常是線程獨(dú)立的,線程的問題應(yīng)該由線程自己來解決踱葛,而不要委托到外部丹莲,也不會(huì)直接影響到其它線程的執(zhí)行。

finally塊和return

首先一個(gè)不容易理解的事實(shí):在 try塊中即便有return尸诽,break甥材,continue等改變執(zhí)行流的語句,finally也會(huì)執(zhí)行逊谋。

public static void main(String[] args)
{
    int re = bar();
    System.out.println(re);
}
private static int bar() 
{
    try{
        return 5;
    } finally{
        System.out.println("finally");
    }
}
/*輸出:
finally
5
*/

很多人面對(duì)這個(gè)問題時(shí)擂达,總是在歸納執(zhí)行的順序和規(guī)律,不過我覺得還是很難理解胶滋。我自己總結(jié)了一個(gè)方法板鬓。用如下GIF圖說明。

image

也就是說:try...catch...finally中的return 只要能執(zhí)行究恤,就都執(zhí)行了俭令,他們共同向同一個(gè)內(nèi)存地址(假設(shè)地址是0x80)寫入返回值,后執(zhí)行的將覆蓋先執(zhí)行的數(shù)據(jù)部宿,而真正被調(diào)用者取的返回值就是最后一次寫入的抄腔。那么,按照這個(gè)思想理张,下面的這個(gè)例子也就不難理解了赫蛇。

finally中的return 會(huì)覆蓋 try 或者catch中的返回值。

public static void main(String[] args)
    {
        int result;
        
        result  =  foo();
        System.out.println(result);     /////////2
        
        result = bar();
        System.out.println(result);    /////////2
    }

    @SuppressWarnings("finally")
    public static int foo()
    {
        trz{
            int a = 5 / 0;
        } catch (Exception e){
            return 1;
        } finally{
            return 2;
        }

    }

    @SuppressWarnings("finally")
    public static int bar()
    {
        try {
            return 1;
        }finally {
            return 2;
        }
    }

finally中的異常會(huì)覆蓋(消滅)前面try或者catch中的異常

class TestException
{
    public static void main(String[] args)
    {
        int result;
        try{
            result = foo();
            System.out.println(result);           //輸出100
        } catch (Exception e){
            System.out.println(e.getMessage());    //沒有捕獲到異常
        }
        
        
        try{
            result  = bar();
            System.out.println(result);           //輸出100
        } catch (Exception e){
            System.out.println(e.getMessage());    //沒有捕獲到異常
        }
    }
    
    //catch中的異常被抑制
    @SuppressWarnings("finally")
    public static int foo() throws Exception
    {
        try {
            int a = 5/0;
            return 1;
        }catch(ArithmeticException amExp) {
            throw new Exception("我將被忽略雾叭,因?yàn)橄旅娴膄inally中使用了return");
        }finally {
            return 100;
        }
    }
    
    //try中的異常被抑制
    @SuppressWarnings("finally")
    public static int bar() throws Exception
    {
        try {
            int a = 5/0;
            return 1;
        }finally {
            return 100;
        }
    }
}

上面的3個(gè)例子都異于常人的編碼思維悟耘,因此我建議:

  • 不要在fianlly中使用return。
  • 不要在finally中拋出異常织狐。
  • 減輕finally的任務(wù)暂幼,不要在finally中做一些其它的事情,finally塊僅僅用來釋放資源是最合適的移迫。
  • 將盡量將所有的return寫在函數(shù)的最后面旺嬉,而不是try ... catch ... finally中。

作者:代碼鋼琴家-lulipro
出處:http://www.cnblogs.com/lulipro/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末厨埋,一起剝皮案震驚了整個(gè)濱河市邪媳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖悲酷,帶你破解...
    沈念sama閱讀 221,820評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件套菜,死亡現(xiàn)場離奇詭異,居然都是意外死亡设易,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門蛹头,熙熙樓的掌柜王于貴愁眉苦臉地迎上來顿肺,“玉大人,你說我怎么就攤上這事渣蜗⊥雷穑” “怎么了?”我有些...
    開封第一講書人閱讀 168,324評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵耕拷,是天一觀的道長讼昆。 經(jīng)常有香客問我,道長骚烧,這世上最難降的妖魔是什么浸赫? 我笑而不...
    開封第一講書人閱讀 59,714評(píng)論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮赃绊,結(jié)果婚禮上既峡,老公的妹妹穿的比我還像新娘。我一直安慰自己碧查,他們只是感情好运敢,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著忠售,像睡著了一般传惠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上稻扬,一...
    開封第一講書人閱讀 52,328評(píng)論 1 310
  • 那天卦方,我揣著相機(jī)與錄音,去河邊找鬼腐螟。 笑死愿汰,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的乐纸。 我是一名探鬼主播衬廷,決...
    沈念sama閱讀 40,897評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼汽绢!你這毒婦竟也來了吗跋?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,804評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎跌宛,沒想到半個(gè)月后酗宋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,345評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡疆拘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評(píng)論 3 340
  • 正文 我和宋清朗相戀三年蜕猫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片哎迄。...
    茶點(diǎn)故事閱讀 40,561評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡回右,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出漱挚,到底是詐尸還是另有隱情翔烁,我是刑警寧澤,帶...
    沈念sama閱讀 36,238評(píng)論 5 350
  • 正文 年R本政府宣布旨涝,位于F島的核電站蹬屹,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏白华。R本人自食惡果不足惜慨默,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望衬鱼。 院中可真熱鬧业筏,春花似錦、人聲如沸鸟赫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽抛蚤。三九已至台谢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間岁经,已是汗流浹背朋沮。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留缀壤,地道東北人樊拓。 一個(gè)月前我還...
    沈念sama閱讀 48,983評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像塘慕,于是被迫代替她去往敵國和親筋夏。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評(píng)論 2 359

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