1. 程式人生 > >【面試】異常相關-這一篇全瞭解

【面試】異常相關-這一篇全瞭解

什麼是Thrownable?

解:

Throwable是java中最頂級的異常類,繼承Object,實現了序列化介面,有兩個重要的子類:Exception和 Error,二者都是 Java 異常處理的重要子類,各自都包含大量子類。

請你談一談對Error和Exception的理解,二者之間有什麼區別和聯絡?

解:

error表示系統級的錯誤,是java執行環境內部錯誤或者硬體問題,不能指望程式來處理這樣的問題,除了退出執行外別無選擇,它是Java虛擬機器丟擲的。

exception 表示程式需要捕捉、需要處理的異常,是由與程式設計的不完善而出現的問題,程式必須處理的問題

Java中Exception分哪兩類,有什麼區別?

解:

Java中的異常,主要可以分為兩大類,即受檢異常(checked exception)和 非受檢異常(unchecked exception)

對於受檢異常來說,如果一個方法在宣告的過程中證明了其要有受檢異常丟擲:

public void test() throw new Exception{ }

那麼,當我們在程式中呼叫他的時候,一定要對該異常進行處理(捕獲或者向上丟擲),否則是無法編譯通過的。這是一種強制規範。

這種異常在IO操作中比較多。比如FileNotFoundException ,當我們使用IO流處理一個檔案的時候,有一種特殊情況,就是檔案不存在,所以,在檔案處理的介面定義時他會顯示丟擲FileNotFoundException,起目的就是告訴這個方法的呼叫者,我這個方法不保證一定可以成功,是有可能找不到對應的檔案的,你要明確的對這種情況做特殊處理哦。

所以說,當我們希望我們的方法呼叫者,明確的處理一些特殊情況的時候,就應該使用受檢異常。

對於非受檢異常來說,一般是執行時異常,繼承自RuntimeException。在編寫程式碼的時候,不需要顯示的捕獲,但是如果不捕獲,在執行期如果發生異常就會中斷程式的執行。

這種異常一般可以理解為是程式碼原因導致的。比如發生空指標、陣列越界等。所以,只要程式碼寫的沒問題,這些異常都是可以避免的。也就不需要我們顯示的進行處理。

試想一下,如果你要對所有可能發生空指標的地方做異常處理的話,那相當於你的所有程式碼都需要做這件事。

Java中異常的處理方式有哪幾種?一般如何選擇。

解:

異常的處理方式有兩種。1、自己處理。2、向上拋,交給呼叫者處理。

異常,千萬不能捕獲了之後什麼也不做。或者只是使用e.printStacktrace。

具體的處理方式的選擇其實原則比較簡明:自己明確的知道如何處理的,就要處理掉。不知道如何處理的,就交給呼叫者處理。

請列舉幾個常用的RuntimeException。

一般面試中java Exception(runtimeException )是必會被問到的問題

常見的異常列出四五種,是基本要求。更多的。。。。需要注意積累了

常見的幾種如下:

NullPointerException - 空指標引用異常
ClassCastException - 型別強制轉換異常。
IllegalArgumentException - 傳遞非法引數異常。
ArithmeticException - 算術運算異常
ArrayStoreException - 向陣列中存放與宣告型別不相容物件異常
IndexOutOfBoundsException - 下標越界異常
NegativeArraySizeException - 建立一個大小為負數的陣列錯誤異常
NumberFormatException - 數字格式異常
SecurityException - 安全異常
UnsupportedOperationException - 不支援的操作異常

 

算術異常類:ArithmeticExecption
空指標異常類:NullPointerException
型別強制轉換異常:ClassCastException
陣列負下標異常:NegativeArrayException
陣列下標越界異常:ArrayIndexOutOfBoundsException
違背安全原則異常:SecturityException
檔案已結束異常:EOFException
檔案未找到異常:FileNotFoundException
字串轉換為數字異常:NumberFormatException
操作資料庫異常:SQLException
輸入輸出異常:IOException
方法未找到異常:NoSuchMethodException

java.lang.AbstractMethodError
抽象方法錯誤。當應用試圖呼叫抽象方法時丟擲。

java.lang.AssertionError
斷言錯。用來指示一個斷言失敗的情況。

java.lang.ClassCircularityError
類迴圈依賴錯誤。在初始化一個類時,若檢測到類之間迴圈依賴則丟擲該異常。

java.lang.ClassFormatError
類格式錯誤。當Java虛擬機器試圖從一個檔案中讀取Java類,而檢測到該檔案的內容不符合類的有效格式時丟擲。

java.lang.Error
錯誤。是所有錯誤的基類,用於標識嚴重的程式執行問題。這些問題通常描述一些不應被應用程式捕獲的反常情況。

java.lang.ExceptionInInitializerError
初始化程式錯誤。當執行一個類的靜態初始化程式的過程中,發生了異常時丟擲。靜態初始化程式是指直接包含於類中的static語句段。

java.lang.IllegalAccessError
違法訪問錯誤。當一個應用試圖訪問、修改某個類的域(Field)或者呼叫其方法,但是又違反域或方法的可見性宣告,則丟擲該異常。

java.lang.IncompatibleClassChangeError
不相容的類變化錯誤。當正在執行的方法所依賴的類定義發生了不相容的改變時,丟擲該異常。一般在修改了應用中的某些類的宣告定義而沒有對整個應用重新編譯而直接執行的情況下,容易引發該錯誤。

java.lang.InstantiationError
例項化錯誤。當一個應用試圖通過Java的new操作符構造一個抽象類或者介面時丟擲該異常.

java.lang.InternalError
內部錯誤。用於指示Java虛擬機發生了內部錯誤。

java.lang.LinkageError
連結錯誤。該錯誤及其所有子類指示某個類依賴於另外一些類,在該類編譯之後,被依賴的類改變了其類定義而沒有重新編譯所有的類,進而引發錯誤的情況。

java.lang.NoClassDefFoundError
未找到類定義錯誤。當Java虛擬機器或者類裝載器試圖例項化某個類,而找不到該類的定義時丟擲該錯誤。

java.lang.NoSuchFieldError
域不存在錯誤。當應用試圖訪問或者修改某類的某個域,而該類的定義中沒有該域的定義時丟擲該錯誤。

java.lang.NoSuchMethodError
方法不存在錯誤。當應用試圖呼叫某類的某個方法,而該類的定義中沒有該方法的定義時丟擲該錯誤。

java.lang.OutOfMemoryError
記憶體不足錯誤。當可用記憶體不足以讓Java虛擬機器分配給一個物件時丟擲該錯誤。

java.lang.StackOverflowError
堆疊溢位錯誤。當一個應用遞迴呼叫的層次太深而導致堆疊溢位時丟擲該錯誤。

java.lang.ThreadDeath
執行緒結束。當呼叫Thread類的stop方法時丟擲該錯誤,用於指示執行緒結束。

java.lang.UnknownError
未知錯誤。用於指示Java虛擬機發生了未知嚴重錯誤的情況。

java.lang.UnsatisfiedLinkError
未滿足的連結錯誤。當Java虛擬機器未找到某個類的宣告為native方法的本機語言定義時丟擲。

java.lang.UnsupportedClassVersionError
不支援的類版本錯誤。當Java虛擬機器試圖從讀取某個類檔案,但是發現該檔案的主、次版本號不被當前Java虛擬機器支援的時候,丟擲該錯誤。

java.lang.VerifyError
驗證錯誤。當驗證器檢測到某個類檔案中存在內部不相容或者安全問題時丟擲該錯誤。

java.lang.VirtualMachineError
虛擬機器錯誤。用於指示虛擬機器被破壞或者繼續執行操作所需的資源不足的情況。

java.lang.ArithmeticException
算術條件異常。譬如:整數除零等。

java.lang.ArrayIndexOutOfBoundsException
陣列索引越界異常。當對陣列的索引值為負數或大於等於陣列大小時丟擲。

java.lang.ArrayStoreException
陣列儲存異常。當向陣列中存放非陣列宣告型別物件時丟擲。

java.lang.ClassCastException
類造型異常。假設有類A和B(A不是B的父類或子類),O是A的例項,那麼當強制將O構造為類B的例項時丟擲該異常。該異常經常被稱為強制型別轉換異常。

java.lang.ClassNotFoundException
找不到類異常。當應用試圖根據字串形式的類名構造類,而在遍歷CLASSPAH之後找不到對應名稱的class檔案時,丟擲該異常。

java.lang.CloneNotSupportedException
不支援克隆異常。當沒有實現Cloneable介面或者不支援克隆方法時,呼叫其clone()方法則丟擲該異常。

java.lang.EnumConstantNotPresentException
列舉常量不存在異常。當應用試圖通過名稱和列舉型別訪問一個列舉物件,但該列舉物件並不包含常量時,丟擲該異常。

java.lang.Exception
根異常。用以描述應用程式希望捕獲的情況。

java.lang.IllegalAccessException
違法的訪問異常。當應用試圖通過反射方式建立某個類的例項、訪問該類屬性、呼叫該類方法,而當時又無法訪問類的、屬性的、方法的或構造方法的定義時丟擲該異常。

java.lang.IllegalMonitorStateException
違法的監控狀態異常。當某個執行緒試圖等待一個自己並不擁有的物件(O)的監控器或者通知其他執行緒等待該物件(O)的監控器時,丟擲該異常。

java.lang.IllegalStateException
違法的狀態異常。當在Java環境和應用尚未處於某個方法的合法呼叫狀態,而呼叫了該方法時,丟擲該異常。

java.lang.IllegalThreadStateException
違法的執行緒狀態異常。當縣城尚未處於某個方法的合法呼叫狀態,而呼叫了該方法時,丟擲異常。

java.lang.IndexOutOfBoundsException
索引越界異常。當訪問某個序列的索引值小於0或大於等於序列大小時,丟擲該異常。

java.lang.InstantiationException
例項化異常。當試圖通過newInstance()方法建立某個類的例項,而該類是一個抽象類或介面時,丟擲該異常。

java.lang.InterruptedException
被中止異常。當某個執行緒處於長時間的等待、休眠或其他暫停狀態,而此時其他的執行緒通過Thread的interrupt方法終止該執行緒時丟擲該異常。

java.lang.NegativeArraySizeException
陣列大小為負值異常。當使用負數大小值建立陣列時丟擲該異常。

java.lang.NoSuchFieldException
屬性不存在異常。當訪問某個類的不存在的屬性時丟擲該異常。

java.lang.NoSuchMethodException
方法不存在異常。當訪問某個類的不存在的方法時丟擲該異常。

java.lang.NullPointerException
空指標異常。當應用試圖在要求使用物件的地方使用了null時,丟擲該異常。譬如:呼叫null物件的例項方法、訪問null物件的屬性、計算null物件的長度、使用throw語句丟擲null等等。

java.lang.NumberFormatException
數字格式異常。當試圖將一個String轉換為指定的數字型別,而該字串確不滿足數字型別要求的格式時,丟擲該異常。

java.lang.RuntimeException
執行時異常。是所有Java虛擬機器正常操作期間可以被丟擲的異常的父類。

java.lang.SecurityException
安全異常。由安全管理器丟擲,用於指示違反安全情況的異常。

java.lang.StringIndexOutOfBoundsException
字串索引越界異常。當使用索引值訪問某個字串中的字元,而該索引值小於0或大於等於序列大小時,丟擲該異常。

java.lang.TypeNotPresentException
型別不存在異常。當應用試圖以某個型別名稱的字串表達方式訪問該型別,但是根據給定的名稱又找不到該型別是丟擲該異常。該異常與ClassNotFoundException的區別在於該異常是unchecked(不被檢查)異常,而ClassNotFoundException是checked(被檢查)異常。

java.lang.UnsupportedOperationException
不支援的方法異常。指明請求的方法不被支援情況的異常。

在處理異常的地方,異常被捕獲以後應該做些什麼?

解:

常見的處理方法有幾種:記錄日誌、封裝異常重新丟擲、忽略、正常返回。

而以上幾種處理中,最不可取的就是忽略。

其他幾種情況,最好伴隨日誌記錄。要把異常資訊記錄下來。

說一說個Java異常處理相關的幾個關鍵字,以及簡單用法。

解:

throws、throw、try、catch、finally

try用來指定一塊預防所有異常的程式;

catch子句緊跟在try塊後面,用來指定你想要捕獲的異常的型別;

finally為確保一段程式碼不管發生什麼異常狀況都要被執行;

throw語句用來明確地丟擲一個異常;

throws用來宣告一個方法可能丟擲的各種異常;

try()裡面有一個return語句,那麼後面的finally{}裡面的code會不會被執行,什麼時候執行,是在return前還是return後?

解:

如果try中有return語句,那麼finally中的程式碼還是會執行。因為return表示的是要整個方法體返回,所以,finally中的語句會在return之前執行 。

1. finally 塊一定會被執行嗎

不一定,需要兩個前提條件:對應 try 語句塊被執行 & 程式正常執行正常執行即 JVM 沒有退出或者執行緒沒有被 killed、interrupted來源:

2. try-catch-finally 與 return 的關係

2.1 try 與 catch 語句塊中有語句: return a + b;

則 finally 語句塊會在 a + b 之後執行,return 之前執行,也就是上面所說的,「返回」前、「操作」之後執行。

2.2 finally 中有 return,直接返回了

注意:在 try 或 catch 中 return a,會將 a 的值壓棧,再彈出儲存到變量表,最終返回新生成的 a 值。因此在 finally 中操作原來的 a 值對結果沒有影響,如果 a 是引用就有影響

栗子:

public int test() {
    int a = 3;
    try {
        return a;
    } finally {
        a = 10;
    }
}
結果返回:3
0: iconst_3
1: istore_1
2: iload_1 將 3 壓棧
3: istore_2 再寫入變量表 2 號位置,即現在有 2 個 a 值
4: bipush 10
6: istore_1
7: iload_2
8: ireturn return 的是新儲存未被改的 2 號位置值
9: astore_3...

什麼是“異常鏈”?

解:

“異常鏈”是Java中非常流行的異常處理概念,是指在進行一個異常處理時丟擲了另外一個異常,由此產生了一個異常鏈條。該技術大多用於將“ 受檢查異常” ( checked exception)封裝成為“非受檢查異常”(unchecked exception)或者RuntimeException。順便說一下,如果因為因為異常你決定丟擲一個新的異常,你一定要包含原有的異常,這樣,處理程式才可以通過getCause()和initCause()方法來訪問異常最終的根源。

什麼是自定義異常,如何使用自定義異常?

解:

自定義異常就是開發人員自己定義的異常,一般通過繼承Exception的子類的方式實現。

編寫自定義異常類實際上是繼承一個API標準異常類,用新定義的異常處理資訊覆蓋原有資訊的過程。

這種用法在Web開發中也比較常見,一般可以用來自定義也無異常。如餘額不足、重複提交等。這種自定義異常有業務含義,更容易讓上層理解和處理。

如果在try/catch塊中。JVM突然中斷了(如使用了System.exit(0)),finally中的程式碼還會執行麼?

解:

不會執行。

什麼是try-with-resources

解:

Java裡,對於檔案操作IO流、資料庫連線等開銷非常昂貴的資源,用完之後必須及時通過close方法將其關閉,否則資源會一直處於開啟狀態,可能會導致記憶體洩露等問題。

關閉資源的常用方式就是在finally塊裡是釋放,即呼叫close方法。比如,我們經常會寫這樣的程式碼:

public static void main(String[] args) {
BufferedReader br = null;
    try {
        String line;
        br = new BufferedReader(new FileReader("d:\\hollischuang.xml"));
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
    } catch (IOException e) {
        // handle exception
    } finally {
        try {
            if (br != null) {
                br.close();
            }
        } catch (IOException ex) {
            // handle exception
        }
    }
}

從Java 7開始,jdk提供了一種更好的方式關閉資源,使用try-with-resources語句,改寫一下上面的程式碼,效果如下:

public static void main(String... args) {
    try (BufferedReader br = new BufferedReader(new FileReader("d:\\ hollischuang.xml"))) {
        String line;
        while ((line = br.readLine()) != null) {
        System.out.println(line);
        }    
    } catch (IOException e) {
        // handle exception
    }
}

看,這簡直是一大福音啊,雖然我之前一般使用IOUtils去關閉流,並不會使用在finally中寫很多程式碼的方式,但是這種新的語法糖看上去好像優雅很多呢。看下他的背後:

public static transient void main(String args[]) {
    BufferedReader br;
    Throwable throwable;
    br = new BufferedReader(new FileReader("d:\\ hollischuang.xml"));
    throwable = null;
    String line;
    try {
        while((line = br.readLine()) != null)
        System.out.println(line);
    } catch(Throwable throwable2) {
        throwable = throwable2;
        throw throwable2;
    }
    if(br != null)
        if(throwable != null)
            try {
                br.close();
            } catch(Throwable throwable1) {
                throwable.addSuppressed(throwable1);
            }
     else
            br.close();
    break MISSING_BLOCK_LABEL_113;
    Exception exception;
    exception;
    if(br != null)
        if(throwable != null)
            try {
                br.close();
            } catch(Throwable throwable3) {
                throwable.addSuppressed(throwable3);
            }
        else
            br.close();
    throw exception;
    IOException ioexception;
    ioexception;
  }
}

其實背後的原理也很簡單,那些我們沒有做的關閉資源的操作,編譯器都幫我們做了。所以,再次印證了,語法糖的作用就是方便程式設計師的使用,但最終還是要轉成編譯器認識的語言。

除了try-with-resources之外,Java7中還對異常做了哪些優化?

解:

1. Multi-Catch Exceptions:catch語句能同時捕獲多個異常。

2. Rethrowing Exceptions:能夠直接再次丟擲已捕獲的異常。

3. Suppressed Exceptions:能夠通過 Throwable.getSuppressed 來獲取被抑制的異常

具體參考連結:Working with Java SE 7 Exception Changes

以下關於異常處理的程式碼有哪些問題?

public static void start() throws IOException, RuntimeException{
    throw new RuntimeException("Not able to Start");
}

public static void main(String args[]) {
    try {
        start();
    } catch (Exception ex) {
        ex.printStackTrace();
    }catch (RuntimeException re) {
        re.printStackTrace();
    }
}

解:

IOException和RuntimeException都不需要使用throws丟擲,首先方法內不會發生IOException,另外RuntimeException為執行時異常,不需要顯示丟擲。

兩個catch需要更換下順序,先catch範圍小的。

另外,在捕獲到異常之後,不能簡單的printStackTrace,需要做進一步處理。