Java提升篇——Java 異常處理

異常的概念

異常是程序中的一些錯誤,但並非全部的錯誤都是異常,而且錯誤有時候是能夠避免的。java

好比說,你的代碼少了一個分號,那麼運行出來結果是提示是錯誤java.lang.Error;若是你用System.out.println(11/0),那麼你是由於你用0作了除數,會拋出java.lang.ArithmeticException的異常。程序員

異常發生的緣由有不少,一般包含如下幾大類:面試

  • 用戶輸入了非法數據。
  • 要打開的文件不存在。
  • 網絡通訊時鏈接中斷,或者JVM內存溢出。

這些異常有的是由於用戶錯誤引發,有的是程序錯誤引發的,還有其它一些是由於物理錯誤引發的。-數據庫

要理解Java異常處理是如何工做的,你須要掌握如下三種類型的異常:編程

  • 檢查性異常:最具表明的檢查性異常是用戶錯誤或問題引發的異常,這是程序員沒法預見的。例如要打開一個不存在文件時,一個異常就發生了,這些異常在編譯時不能被簡單地忽略。
  • 運行時異常: 運行時異常是可能被程序員避免的異常。與檢查性異常相反,運行時異常能夠在編譯時被忽略。
  • 錯誤: 錯誤不是異常,而是脫離程序員控制的問題。錯誤在代碼中一般被忽略。例如,當棧溢出時,一個錯誤就發生了,它們在編譯也檢查不到的。

 

異常指不期而至的各類情況,如:文件找不到、網絡鏈接失敗、除0操做、非法參數等。異常是一個事件,它發生在程序運行期間,干擾了正常的指令流程。數組

Java語言在設計的當初就考慮到這些問題,提出異常處理的框架的方案,全部的異常均可以用一個異常類來表示,不一樣類型的異常對應不一樣的子類異常(目前咱們所說的異常包括錯誤概念),定義異常處理的規範,在JDK1.4版本之後增長了異常鏈機制,從而便於跟蹤異常。安全

Java異常是一個描述在代碼段中發生異常的對象,當發生異常狀況時,一個表明該異常的對象被建立而且在致使該異常的方法中被拋出,而該方法能夠選擇本身處理異常或者傳遞該異常。網絡

異常的體系結構

Java把異常看成對象來處理,並定義一個基類java.lang.Throwable做爲全部異常的超類。框架

在Java API中已經定義了許多異常類,這些異常類分爲兩大類,錯誤Error和異常Exceptionide

Java異常層次結構圖以下圖所示:

從圖中能夠看出全部異常類型都是內置類Throwable的子類,於是Throwable在異常類的層次結構的頂層。

接下來Throwable分紅了兩個不一樣的分支,一個分支是Error,它表示不但願被程序捕獲或者是程序沒法處理的錯誤另外一個分支是Exception,它表示用戶程序可能捕捉的異常狀況或者說是程序能夠處理的異常。其中異常類Exception又分爲運行時異常(RuntimeException)和非運行時異常。

Java異常又能夠分爲不受檢查異常(Unchecked Exception)和檢查異常(Checked Exception)。

 

下面將詳細講述這些異常之間的區別與聯繫:

  • ErrorError類對象由 Java 虛擬機生成並拋出,大多數錯誤與代碼編寫者所執行的操做無關例如,Java虛擬機運行錯誤(Virtual MachineError),當JVM再也不有繼續執行操做所需的內存資源時,將出現 OutOfMemoryError。這些異常發生時,Java虛擬機(JVM)通常會選擇線程終止;還有發生在虛擬機試圖執行應用時,如類定義錯誤(NoClassDefFoundError)、連接錯誤(LinkageError)。這些錯誤是不可查的,由於它們在應用程序的控制和處理能力之 外,並且絕大多數是程序運行時不容許出現的情況。對於設計合理的應用程序來講,即便確實發生了錯誤,本質上也不該該試圖去處理它所引發的異常情況。在Java中,錯誤一般是使用Error的子類描述。
  • Exception:在Exception分支中有一個重要的子類RuntimeException(運行時異常),該類型的異常自動爲你所編寫的程序定義ArrayIndexOutOfBoundsException(數組下標越界)、NullPointerException(空指針異常)、ArithmeticException(算術異常)、MissingResourceException(丟失資源)、ClassNotFoundException(找不到類)等異常,這些異常是不檢查異常,程序中能夠選擇捕獲處理,也能夠不處理。這些異常通常是由程序邏輯錯誤引發的,程序應該從邏輯角度儘量避免這類異常的發生;而RuntimeException以外的異常咱們統稱爲非運行時異常,類型上屬於Exception類及其子類,從程序語法角度講是必須進行處理的異常,若是不處理,程序就不能編譯經過。如IOExceptionSQLException等以及用戶自定義的Exception異常,通常狀況下不自定義檢查異常。

 注意

ErrorException的區別:Error一般是災難性的致命的錯誤,是程序沒法控制和處理的,當出現這些異常時,Java虛擬機(JVM)通常會選擇終止線程;Exception一般狀況下是能夠被程序處理的,而且在程序中應該儘量的去處理這些異常。

  • 檢查異常:在正確的程序運行過程當中,很容易出現的、情理可容的異常情況,在必定程度上這種異常的發生是能夠預測的,而且一旦發生該種異常,就必須採起某種方式進行處理。

♠提示

除了RuntimeException及其子類之外,其餘的Exception類及其子類都屬於檢查異常,當程序中可能出現這類異常,要麼使用try-catch語句進行捕獲,要麼用throws子句拋出,不然編譯沒法經過。

  • 不受檢查異常包括RuntimeException及其子類和Error

♠提示

不受檢查異常爲編譯器不要求強制處理的異常,檢查異常則是編譯器要求必須處置的異常。

Java 異常的處理機制

Java的異常處理本質上是拋出異常捕獲異常

  • 拋出異常:要理解拋出異常,首先要明白什麼是異常情形(exception condition),它是指阻止當前方法或做用域繼續執行的問題。其次把異常情形和普通問題相區分,普通問題是指在當前環境下能獲得足夠的信息,總能處理這個錯誤。對於異常情形,已經沒法繼續下去了,由於在當前環境下沒法得到必要的信息來解決問題,你所能作的就是從當前環境中跳出,並把問題提交給上一級環境,這就是拋出異常時所發生的事情。拋出異常後,會有幾件事隨之發生。首先,是像建立普通的java對象同樣將使用new在堆上建立一個異常對象;而後,當前的執行路徑(已經沒法繼續下去了)被終止,而且從當前環境中彈出對異常對象的引用。此時,異常處理機制接管程序,並開始尋找一個恰當的地方繼續執行程序,這個恰當的地方就是異常處理程序或者異常處理器,它的任務是將程序從錯誤狀態中恢復,以使程序要麼換一種方式運行,要麼繼續運行下去。

舉個簡單的例子,假使咱們建立了一個學生對象Student的一個引用stu,在調用的時候可能尚未初始化。因此在使用這個對象引用調用其餘方法以前,要先對它進行檢查,能夠建立一個表明錯誤信息的對象,而且將它從當前環境中拋出,這樣就把錯誤信息傳播到更大的環境中。

if(stu == null){
    throw new NullPointerException();
}

這就拋出了異常,它將在其餘的地方獲得執行或者處理,具體是哪一個地方後面將很快介紹,代碼中出現的 throw 是一個關鍵字,暫時先不作過多講解,後面會詳細講解。

  • 捕獲異常:在方法拋出異常以後,運行時系統將轉爲尋找合適的異常處理器(exception handler)。潛在的異常處理器是異常發生時依次存留在調用棧中的方法的集合。當異常處理器所能處理的異常類型與方法拋出的異常類型相符時,即爲合適的異常處理器。運行時系統從發生異常的方法開始,依次回查調用棧中的方法,直至找到含有合適異常處理器的方法並執行。當運行時系統遍歷調用棧而未找到合適的異常處理器,則運行時系統終止。同時,意味着Java程序的終止。

 提示

對於運行時異常錯誤檢查異常,Java技術所要求的異常處理方式有所不一樣。

因爲運行時異常及其子類的不可查性,爲了更合理、更容易地實現應用程序,Java規定,運行時異常將由Java運行時系統自動拋出,容許應用程序忽略運行時異常

對於方法運行中可能出現的Error,當運行方法不欲捕捉時,Java容許該方法不作任何拋出聲明。由於,大多數Error異常屬於永遠不能被容許發生的情況,也屬於合理的應用程序不應捕捉的異常。

對於全部的檢查異常,Java規定:一個方法必須捕捉,或者聲明拋出方法以外。也就是說,當一個方法選擇不捕捉檢查異常時,它必須聲明將拋出異常。

Java異常處理涉及到五個關鍵字,分別是:trycatchfinallythrowthrows。下面將驟一介紹,經過認識這五個關鍵字,掌握基本異常處理知識。

  • try        -- 用於監聽。將要被監聽的代碼(可能拋出異常的代碼)放在try語句塊以內,當try語句塊內發生異常時,異常就被拋出。
  • catch   -- 用於捕獲異常。catch用來捕獲try語句塊中發生的異常。
  • finally  -- finally語句塊老是會被執行。它主要用於回收在try塊裏打開的物力資源(如數據庫鏈接、網絡鏈接和磁盤文件)。只有finally塊,執行完成以後,纔會回來執行try或者catch塊中的return或者throw語句,若是finally中使用了return或者throw等終止方法的語句,則就不會跳回執行,直接中止。
  • throw   -- 用於拋出異常。
  • throws -- 用在方法簽名中,用於聲明該方法可能拋出的異常。

異常處理的基本語法

1. try-catch

try{
    //code that might generate exceptions    
}catch(Exception e){
    //the code of handling exception1
}catch(Exception e){
    //the code of handling exception2
}

要明白異常捕獲,還要理解監控區域(guarded region)的概念。它是一段可能產生異常的代碼,而且後面跟着處理這些異常的代碼。

由此可見,上述try-catch所描述的便是監控區域,關鍵詞try後的一對大括號將一塊可能發生異常的代碼包起來,即爲監控區域。Java方法在運行過程當中發生了異常,則建立異常對象。將異常拋出監控區域以外,由Java運行時系統負責尋找匹配的catch子句來捕獲異常。如有一個catch語句匹配到了,則執行該catch塊中的異常處理代碼,就再也不嘗試匹配別的catch塊了。

匹配的原則是:若是拋出的異常對象屬於catch子句的異常類,或者屬於該異常類的子類,則認爲生成的異常對象與catch塊捕獲的異常類型相匹配。

舉個例子算術異常:

public class TestException {  
    public static void main(String[] args) {  
        int a = 1;  
        int b = 0;  
        try { // try監控區域               
            if (b == 0) throw new ArithmeticException(); // 經過throw語句拋出異常  
            System.out.println("a/b的值是:" + a / b);  
            System.out.println("this will not be printed!");
        }  
        catch (ArithmeticException e) { // catch捕捉異常  
            System.out.println("程序出現異常,變量b不能爲0!");  
        }  
        System.out.println("程序正常結束。");  
    }  
}  

運行結果:

D:\java>java TestException
 
程序出現異常,變量b不能爲0!

程序正常結束。

顯示一個異常的描述,Throwable重載了toString()方法(由Object定義),因此它將返回一個包含異常描述的字符串。例如,將前面的catch塊重寫成:

catch (ArithmeticException e) { // catch捕捉異常  
    System.out.println("程序出現異常"+e);  
} 

結果:

D:\java>java TestException

程序出現異常java.lang.ArithmeticException

程序正常結束。

根據前面講述的,算術異常屬於運行時異常,於是實際上該異常不須要程序拋出,運行時系統自動拋出,將例子改成以下:

public class TestException {  
    public static void main(String[] args) {  
        int a = 1;  
        int b = 0;    
        System.out.println("a/b的值是:" + a / b);
        System.out.println("this will not be printed!");
    }  
}  

結果:

D:\java>java TestException

Exception in thread "main" java.lang.ArithmeticException: / by zero
    at TestException.main(TestException.java:7)

使用多重的catch語句:不少狀況下,由單個的代碼段可能引發多個異常。處理這種狀況,咱們須要定義兩個或者更多的catch子句,每一個子句捕獲一種類型的異常,當異常被引起時,每一個catch子句被依次檢查,第一個匹配異常類型的子句執行,當一個catch子句執行之後,其餘的子句將被旁路。

編寫多重catch語句塊注意事項:

  順序問題:先小後大,即先子類後父類

 

Java經過異常類描述異常類型。對於有多個catch子句的異常程序而言,應該儘可能將捕獲底層異常類的catch子句放在前面,同時儘可能將捕獲相對高層的異常類的catch子句放在後面。不然,捕獲底層異常類的catch子句將可能會被屏蔽。

RuntimeException異常類包括運行時各類常見的異常,ArithmeticException類和ArrayIndexOutOfBoundsException類都是它的子類。所以,RuntimeException異常類的catch子句應該放在最後面,不然可能會屏蔽其後的特定異常處理或引發編譯錯誤。

嵌套try語句try語句能夠被嵌套。也就是說,一個try語句能夠在另外一個try塊的內部。每次進入try語句,異常的先後關係都會被推入堆棧。若是一個內部的try語句不含特殊異常的catch處理程序,堆棧將彈出,下一個try語句的catch處理程序將檢查是否與之匹配。這個過程將繼續直到一個catch語句被匹配成功,或者是直到全部的嵌套try語句被檢查完畢。若是沒有catch語句匹配,Java運行時系統將處理這個異常。

例如:

class NestTry{
    public static void main(String[] args){
        try{
            int a = args.length;
            int b = 42 / a;
            System.out.println("a = "+ a);
            try{
                if(a == 1){
                a = a/(a-a);
                }
                if(a == 2){
                    int c[] = {1};
                    c[42] =99;
                }
            }catch(ArrayIndexOutOfBoundsException e){
                System.out.println("ArrayIndexOutOfBounds :"+e);
            }    
        }catch(ArithmeticException e){
            System.out.println("Divide by 0"+ e);
        }
    }
}

正如程序中所顯示的,該程序在一個try塊中嵌套了另外一個try塊。程序工做以下:當你在沒有命令行參數的狀況下執行該程序,外面的try塊將產生一個被0除的異常。程序在有一個命令行參數條件下執行,由嵌套的try塊產生一個被0除的異常,因爲內部的catch塊不匹配這個異常,它將把異常傳給外部的try塊,在外部異常被處理。若是你在具備兩個命令行參數的條件下執行該程序,將由內部try塊產生一個數組邊界異常。

結果:

D:\java>javac estTry.java

D:\java>>java NestTry

Divide by 0 java.lang.ArithmeticExceptio: / by zero

D:\java>java NestTry one

a = 1

Divide by 0java.lang.ArithmeticException: / by zero

D:\java>java NestTry one two

a = 2

ArrayIndexOutOfBounds :java.lang.ArrayIndexOutOfBoundsException: 42

注意:當有方法調用時,try語句的嵌套能夠很隱蔽的發生。例如,咱們能夠將對方法的調用放在一個try塊中。在該方法的內部,有另外一個try語句。在這種狀況下,方法內部的try仍然是嵌套在外部調用該方法的try塊中的。下面咱們將對上述例子進行修改,嵌套的try塊移到方法nesttry()的內部:

class NestTry{
    static void nesttry(int a){
        try{
            if(a == 1){
                a = a/(a-a);
            }
            if(a == 2){
                int c[] = {1};
                c[42] =99;
            }
        }catch(ArrayIndexOutOfBoundsException e){
            System.out.println("ArrayIndexOutOfBounds :"+e);
        }    
    }
    public static void main(String[] args){
        try{
            int a = args.length;
            int b = 42 / a;
            System.out.println("a = "+ a);
            nesttry(a);
        }catch(ArithmeticException e){
            System.out.println("Divide by 0"+ e);
        }
    }
}

結果輸出與前面例子一致:

D:\java>javac NestTry.java

D:\java>java NestTry

Divide by 0java.lang.ArithmeticException: / by zero

D:\java>java NestTry one

a = 1

Divide by 0java.lang.ArithmeticException: / by zero

D:\java>java NestTry one two

a = 2

ArrayIndexOutOfBounds :java.lang.ArrayIndexOutOfBoundsException: 42

2. throw

到目前爲止,咱們只是獲取了被Java運行時系統引起的異常。然而,咱們還能夠用throw語句拋出明確的異常。Throw的語法形式以下:

throw ThrowableInstance;

這裏的ThrowableInstance必定是Throwable類類型或者Throwable子類類型的一個對象。簡單的數據類型,例如intchar,以及非Throwable類,例如StringObject,不能用做異常。有兩種方法能夠獲取Throwable對象:在catch子句中使用參數或者使用new操做符建立。

程序執行完throw語句以後當即中止;throw後面的任何語句不被執行,最鄰近的try塊用來檢查它是否含有一個與異常類型匹配的catch語句。若是發現了匹配的塊,控制轉向該語句;若是沒有發現,次包圍的try塊來檢查,以此類推。若是沒有發現匹配的catch塊,默認異常處理程序中斷程序的執行而且打印堆棧軌跡。

例如:

class TestThrow{
    static void proc(){
        try{
            throw new NullPointerException("demo");
        }catch(NullPointerException e){
            System.out.println("Caught inside proc");
            throw e;
        }
    }

    public static void main(String [] args){
        try{
            proc();
        }catch(NullPointerException e){
            System.out.println("Recaught: "+e);
        }
    }
}

結果:

D:\java>java TestThrow

Caught inside proc

Recaught: java.lang.NullPointerException: demo

該程序兩次處理相同的錯誤,首先,main()方法設立了一個異常關係而後調用proc()。proc()方法設立了另外一個異常處理關係而且當即拋出一個NullPointerException實例,NullPointerExceptionmain()中被再次捕獲。

該程序闡述了怎樣建立Java的標準異常對象,特別注意這一行:

throw new NullPointerException("demo");

此處new用來構造一個NullPointerException實例,全部的Java內置的運行時異常有兩個構造方法:一個沒有參數,一個帶有一個字符串參數。當用第二種形式時,參數指定描述異常的字符串。若是對象用做print()或者println()的參數時,該字符串被顯示。這一樣能夠經過調用getMessage()來實現,getMessage()是由Throwable定義的。

3. throws

若是一個方法能夠致使一個異常但不處理它,它必須指定這種行爲以使方法的調用者能夠保護它們本身而不發生異常。要作到這點,咱們能夠在方法聲明中包含一個throws子句。一個throws子句列舉了一個方法可能引起的全部異常類型。這對於除了ErrorRuntimeException及它們子類之外類型的全部異常是必要的。一個方法能夠引起的全部其餘類型的異常必須在throws子句中聲明,不然會致使編譯錯誤。

下面是throws子句的方法聲明的通用形式:

public void info() throws Exception
{
   //body of method
}

Exception 是該方法可能引起的全部的異常,也能夠是異常列表,中間以逗號隔開。

例如:

class TestThrows{
    static void throw1(){
        System.out.println("Inside throw1 . ");
        throw new IllegalAccessException("demo");
    }
    public static void main(String[] args){
        throw1();
    }
}

上述例子中有兩個地方存在錯誤,你能看出來嗎?

該例子中存在兩個錯誤,首先,throw1()方法不想處理所致使的異常,於是它必須聲明throws子句來列舉可能引起的異常即IllegalAccessException;其次,main()方法必須定義try/catch語句來捕獲該異常。

正確例子以下:

class TestThrows{
    static void throw1() throws IllegalAccessException {
        System.out.println("Inside throw1 . ");
        throw new IllegalAccessException("demo");
    }
    public static void main(String[] args){
        try {
            throw1();
        }catch(IllegalAccessException e ){
            System.out.println("Caught " + e);
        }
    }
}

Throws拋出異常的規則:

  • 若是是不受檢查異常(unchecked exception),即ErrorRuntimeException或它們的子類,那麼能夠不使用throws關鍵字來聲明要拋出的異常,編譯仍能順利經過,但在運行時會被系統拋出。
  • 必須聲明方法可拋出的任何檢查異常(checked exception)。即若是一個方法可能出現受可查異常,要麼用try-catch語句捕獲,要麼用throws子句聲明將它拋出,不然會致使編譯錯誤
  • 僅當拋出了異常,該方法的調用者才必須處理或者從新拋出該異常。當方法的調用者無力處理該異常的時候,應該繼續拋出,而不是囫圇吞棗。
  • 調用方法必須遵循任何可查異常的處理和聲明規則。若覆蓋一個方法,則不能聲明與覆蓋方法不一樣的異常。聲明的任何異常必須是被覆蓋方法所聲明異常的同類或子類。

4. finally

當異常發生時,一般方法的執行將作一個陡峭的非線性的轉向,它甚至會過早的致使方法返回。例如,若是一個方法打開了一個文件並關閉,而後退出,你不但願關閉文件的代碼被異常處理機制旁路。finally關鍵字爲處理這種意外而設計。

finally建立的代碼塊在try/catch塊完成以後另外一個try/catch出現以前執行。finally塊不管有沒有異常拋出都會執行。若是拋出異常,即便沒有catch子句匹配,finally也會執行。一個方法將從一個try/catch塊返回到調用程序的任什麼時候候,通過一個未捕獲的異常或者是一個明確的返回語句,finally子句在方法返回以前仍將執行。這在關閉文件句柄和釋聽任何在方法開始時被分配的其餘資源是頗有用。

finally子句是可選項,能夠有也能夠無,可是每一個try語句至少須要一個catch或者finally子句。

class TestFinally{
    static void proc1(){
        try{
            System.out.println("inside proc1");
            throw new RuntimeException("demo");
        }finally{
            System.out.println("proc1's finally");
        }
    }
    static void proc2(){
        try{
            System.out.println("inside proc2");
            return ;
        } finally{
            System.out.println("proc2's finally");
        }
    } 
    static void proc3(){
        try{
            System.out.println("inside proc3");
        }finally{
            System.out.println("proc3's finally");
        }
    }
    public static void main(String [] args){
        try{
            proc1();
        }catch(Exception e){
            System.out.println("Exception caught");
        }
        proc2();
        proc3();
    }
}

該例子中,proc1()拋出了異常中斷了try,它的finally子句在退出時執行。proc2的try語句經過return語句返回,但在返回以前finally語句執行。在proc3()中try語句正常執行,沒有錯誤,finally語句也被執行。

輸出結果:

D:\java>java TestFinally

inside proc1

proc1's finally

Exception caught

inside proc2
 
proc2's finally
 
inside proc3
 
proc3's finally

注:若是finally塊與一個try聯合使用,finally塊將在try結束以前執行。

問題擴展(面試題)

一、try{} 裏有一個 return 語句,那麼緊跟在這個 try 後的 finally{} 裏的 code 會不會被執行,何時被執行,在 return 前仍是後?

答案:會執行,在方法返回調用者前執行。

注意:在finally中改變返回值的作法是很差的,由於若是存在finally代碼塊,try中的return語句不會立馬返回調用者,而是記錄下返回值待finally代碼塊執行完畢以後再向調用者返回其值,而後若是在finally中修改了返回值,就會返回修改後的值。顯然,在finally中返回或者修改返回值會對程序形成很大的困擾,C#中直接用編譯錯誤的方式來阻止程序員幹這種齷齪的事情,Java中也能夠經過提高編譯器的語法檢查級別來產生警告或錯誤,Eclipse中能夠在如圖所示的地方進行設置,強烈建議將此項設置爲編譯錯誤。

 

二、Java語言如何進行異常處理,關鍵字:throws、throw、try、catch、finally分別如何使用?

答:Java經過面向對象的方法進行異常處理,把各類不一樣的異常進行分類,並提供了良好的接口。在Java中,每一個異常都是一個對象,它是Throwable類或其子類的實例。當一個方法出現異常後便拋出一個異常對象,該對象中包含有異常信息,調用這個對象的方法能夠捕獲到這個異常並能夠對其進行處理。Java的異常處理是經過5個關鍵詞來實現的:try、catch、throw、throws和finally。通常狀況下是用try來執行一段程序,若是系統會拋出(throw)一個異常對象,能夠經過它的類型來捕獲(catch)它,或經過老是執行代碼塊(finally)來處理;try用來指定一塊預防全部異常的程序;catch子句緊跟在try塊後面,用來指定你想要捕獲的異常的類型;throw語句用來明確地拋出一個異常;throws用來聲明一個方法可能拋出的各類異常(固然聲明異常時容許無病呻吟);finally爲確保一段代碼無論發生什麼異常情況都要被執行;try語句能夠嵌套,每當遇到一個try語句,異常的結構就會被放入異常棧中,直到全部的try語句都完成。若是下一級的try語句沒有對某種異常進行處理,異常棧就會執行出棧操做,直到遇到有處理這種異常的try語句或者最終將異常拋給JVM。

三、運行時異常與受檢異常有何異同? 

答:異常表示程序運行過程當中可能出現的非正常狀態,運行時異常表示虛擬機的一般操做中可能遇到的異常,是一種常見運行錯誤,只要程序設計得沒有問題一般就不會發生。受檢異常跟程序運行的上下文環境有關,即便程序設計無誤,仍然可能因使用的問題而引起。Java編譯器要求方法必須聲明拋出可能發生的受檢異常,可是並不要求必須聲明拋出未被捕獲的運行時異常。異常和繼承同樣,是面向對象程序設計中常常被濫用的東西,在Effective Java中對異常的使用給出瞭如下指導原則: 
- 不要將異常處理用於正常的控制流(設計良好的API不該該強迫它的調用者爲了正常的控制流而使用異常) 
- 對能夠恢復的狀況使用受檢異常,對編程錯誤使用運行時異常 
- 避免沒必要要的使用受檢異常(能夠經過一些狀態檢測手段來避免異常的發生) 
- 優先使用標準的異常 
- 每一個方法拋出的異常都要有文檔 
- 保持異常的原子性 
- 不要在catch中忽略掉捕獲到的異常

四、列出一些你常見的運行時異常? 

答: 
- ArithmeticException(算術異常) 
- ClassCastException (類轉換異常) 
- IllegalArgumentException (非法參數異常) 
- IndexOutOfBoundsException (下標越界異常) 
- NullPointerException (空指針異常) 
- SecurityException (安全異常)

 

異常鏈

異常鏈顧名思義就是將異常發生的緣由一個傳一個串起來,即把底層的異常信息傳給上層,這樣逐層拋出。 Java API文檔中給出了一個簡單的模型:

try {   
    lowLevelOp();   
} catch (LowLevelException le) {   
    throw (HighLevelException) new HighLevelException().initCause(le);   
}

當程序捕獲到了一個底層異常,在處理部分選擇了繼續拋出一個更高級別的新異常給此方法的調用者。 這樣異常的緣由就會逐層傳遞。這樣,位於高層的異常遞歸調用getCause()方法,就能夠遍歷各層的異常緣由。 這就是Java異常鏈的原理。異常鏈的實際應用不多,發生異常時候逐層上拋不是個好注意, 上層拿到這些異常又能奈之何?並且異常逐層上拋會消耗大量資源, 由於要保存一個完整的異常鏈信息.

自定義異常

使用Java內置的異常類能夠描述在編程時出現的大部分異常狀況。除此以外,用戶還能夠自定義異常。用戶自定義異常類,只需繼承Exception類便可。

在程序中使用自定義異常類,大致可分爲如下幾個步驟:

  • 建立自定義異常類。
  • 在方法中經過throw關鍵字拋出異常對象。
  • 若是在當前拋出異常的方法中處理異常,可使用try-catch語句捕獲並處理;不然在方法的聲明處經過throws關鍵字指明要拋出給方法調用者的異常,繼續進行下一步操做。
  • 在出現異常方法的調用者中捕獲並處理異常。

舉例自定義異常:

class MyException extends Exception {
    private int detail;
    MyException(int a){
        detail = a;
    }
    public String toString(){
        return "MyException ["+ detail + "]";
    }
}
public class TestMyException{
    static void compute(int a) throws MyException{
        System.out.println("Called compute(" + a + ")");
        if(a > 10){
            throw new MyException(a);
        }
        System.out.println("Normal exit!");
    }
    public static void main(String [] args){
        try{
            compute(1);
            compute(20);
        }catch(MyException me){
            System.out.println("Caught " + me);
        }
    }
}

該例子徹底按照上述步驟。

運行結果以下:
D:\java>java TestMyException

Called compute(1)

Normal exit!

Called compute(20)

Caught MyException [20]

總結

 


參考文檔:

Java入門之異常處理

相關文章
相關標籤/搜索