沒錯,看完這篇Exception 和 Error,和面試官扯皮就沒問題了

2021-03-06 碼農有道



在 Java 中的基本理念是 結構不佳的代碼不能運行,發現錯誤的理想時期是在編譯期間,因為你不用運行程序,只是憑藉著對 Java 基本理念的理解就能發現問題。但是編譯期並不能找出所有的問題,有一些 NullPointerException 和 ClassNotFoundException 在編譯期找不到,這些異常是 RuntimeException 運行時異常,這些異常往往在運行時才能被發現。

我們寫 Java 程序經常會出現兩種問題,一種是 java.lang.Exception ,一種是 java.lang.Error,都用來表示出現了異常情況,下面就針對這兩種概念進行理解。

認識 Exception

Exception 位於 java.lang 包下,它是一種頂級接口,繼承於 Throwable 類,Exception 類及其子類都是 Throwable 的組成條件,是程序出現的合理情況。

在認識 Exception 之前,有必要先了解一下什麼是 Throwable。

什麼是 Throwable

Throwable 類是 Java 語言中所有錯誤(errors)和異常(exceptions)的父類。只有繼承於 Throwable 的類或者其子類才能夠被拋出,還有一種方式是帶有 Java 中的 @throw 註解的類也可以拋出。

在Java規範中,對非受查異常和受查異常的定義是這樣的:

The unchecked exception classes are the run-time exception classes and the error classes.

The checked exception classes are all exception classes other than the unchecked exception classes. That is, the checked exception classes are Throwable and all its subclasses other than RuntimeException and its subclasses and Errorand its subclasses.

也就是說,除了 RuntimeException 和其子類,以及error和其子類,其它的所有異常都是 checkedException。

那麼,按照這種邏輯關係,我們可以對 Throwable 及其子類進行歸類分析

可以看到,Throwable 位於異常和錯誤的最頂層,我們查看 Throwable 類中發現它的方法和屬性有很多,我們只討論其中幾個比較常用的

// 返回拋出異常的詳細信息
public string getMessage();
public string getLocalizedMessage();

//返回異常發生時的簡要描述
public public String toString();
  
// 列印異常信息到標準輸出流上
public void printStackTrace();
public void printStackTrace(PrintStream s);
public void printStackTrace(PrintWriter s)

// 記錄棧幀的的當前狀態
public synchronized Throwable fillInStackTrace();

此外,因為 Throwable 的父類也是 Object,所以常用的方法還有繼承其父類的getClass() 和 getName() 方法。

常見的 Exception

下面我們回到 Exception 的探討上來,現在你知道了 Exception 的父類是 Throwable,並且 Exception 有兩種異常,一種是 RuntimeException ;一種是 CheckedException,這兩種異常都應該去捕獲。

下面列出了一些 Java 中常見的異常及其分類,這塊面試官也可能讓你舉出幾個常見的異常情況並將其分類

RuntimeException

序號異常名稱異常描述1ArrayIndexOutOfBoundsException數組越界異常2NullPointerException空指針異常3IllegalArgumentException非法參數異常4NegativeArraySizeException數組長度為負異常5IllegalStateException非法狀態異常6ClassCastException類型轉換異常

UncheckedException

序號異常名稱異常描述1NoSuchFieldException表示該類沒有指定名稱拋出來的異常2NoSuchMethodException表示該類沒有指定方法拋出來的異常3IllegalAccessException不允許訪問某個類的異常4ClassNotFoundException類沒有找到拋出異常與 Exception 有關的 Java 關鍵字

那麼 Java 中是如何處理這些異常的呢?在 Java 中有這幾個關鍵字 throws、throw、try、finally、catch 下面我們分別來探討一下

throws 和 throw

在 Java 中,異常也就是一個對象,它能夠被程式設計師自定義拋出或者應用程式拋出,必須藉助於 throws 和 throw 語句來定義拋出異常。

throws 和 throw 通常是成對出現的,例如

static void cacheException() throws Exception{

  throw new Exception();

}

throw 語句用在方法體內,表示拋出異常,由方法體內的語句處理。throws 語句用在方法聲明後面,表示再拋出異常,由該方法的調用者來處理。

throws 主要是聲明這個方法會拋出這種類型的異常,使它的調用者知道要捕獲這個異常。throw 是具體向外拋異常的動作,所以它是拋出一個異常實例。

try 、finally 、catch

這三個關鍵字主要有下面幾種組合方式 try...catch 、try...finally、try...catch...finally

try...catch 表示對某一段代碼可能拋出異常進行的捕獲,如下

static void cacheException() throws Exception{

  try {
    System.out.println("1");
  }catch (Exception e){
    e.printStackTrace();
  }

}

try...finally 表示對一段代碼不管執行情況如何,都會走 finally 中的代碼

static void cacheException() throws Exception{
  for (int i = 0; i < 5; i++) {
    System.out.println("enter: i=" + i);
    try {
      System.out.println("execute: i=" + i);
      continue;
    } finally {
      System.out.println("leave: i=" + i);
    }
  }
}

try...catch...finally 也是一樣的,表示對異常捕獲後,再走 finally 中的代碼邏輯。

JDK1.7 使用 try...with...resources 優雅關閉資源

Java 類庫中有許多資源需要通過 close 方法進行關閉。比如 InputStream、OutputStream,資料庫連接對象 Connection,MyBatis 中的 SqlSession 會話等。作為開發人員經常會忽略掉資源的關閉方法,導致內存洩漏。

根據經驗,try-finally語句是確保資源會被關閉的最佳方法,就算異常或者返回也一樣。try-catch-finally 一般是這樣來用的

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

這樣看起來代碼還是比較整潔,但是當我們添加第二個需要關閉的資源的時候,就像下面這樣

static void copy(String src,String dst) throws Exception{
        InputStream is = new FileInputStream(src);
  try {

    OutputStream os = new FileOutputStream(dst);
    try {
      byte[] buf = new byte[100];
      int n;
      while ((n = is.read()) >= 0){
        os.write(buf,n,0);
      }
    }finally {
      os.close();
    }
  }finally {
    is.close();
  }
}

這樣感覺這個方法已經變得臃腫起來了。

而且這種寫法也存在諸多問題,即使 try - finally 能夠正確關閉資源,但是它不能阻止異常的拋出,因為 try 和 finally 塊中都可能有異常的發生。

比如說你正在讀取的時候硬碟損壞,這個時候你就無法讀取文件和關閉資源了,此時會拋出兩個異常。但是在這種情況下,第二個異常會抹掉第一個異常。在異常堆棧中也無法找到第一個異常的記錄,怎麼辦,難道像這樣來捕捉異常麼?

static void tryThrowException(String path) throws Exception {

  BufferedReader br = new BufferedReader(new FileReader(path));
  try {
    String s = br.readLine();
    System.out.println("s = " + s);

  }catch (Exception e){
    e.printStackTrace();
  }finally {
    try {
      br.close();
    }catch (Exception e){
      e.printStackTrace();
    }finally {
      br.close();
    }
  }
}

這種寫法,雖然能解決異常拋出的問題,但是各種 try-cath-finally 的嵌套會讓代碼變得非常臃腫。

Java7 中引入了try-with-resources 語句時,所有這些問題都能得到解決。要使用 try-with-resources 語句,首先要實現 AutoCloseable 接口,此接口包含了單個返回的 close 方法。Java 類庫與三方類庫中的許多類和接口,現在都實現或者擴展了 AutoCloseable 接口。如果編寫了一個類,它代表的是必須關閉的資源,那麼這個類應該實現 AutoCloseable 接口。

java 引入了 try-with-resources 聲明,將 try-catch-finally 簡化為 try-catch,這其實是一種語法糖,在編譯時會進行轉化為 try-catch-finally 語句。

下面是使用 try-with-resources 的第一個範例

/**
     * 使用try-with-resources 改寫示例一
     * @param path
     * @return
     * @throws IOException
     */
static String firstLineOfFileAutoClose(String path) throws IOException {

  try(BufferedReader br = new BufferedReader(new FileReader(path))){
    return br.readLine();
  }
}

使用 try-with-resources 改寫程序的第二個示例

static void copyAutoClose(String src,String dst) throws IOException{

  try(InputStream in = new FileInputStream(src);
      OutputStream os = new FileOutputStream(dst)){
    byte[] buf = new byte[1000];
    int n;
    while ((n = in.read(buf)) >= 0){
      os.write(buf,0,n);
    }
  }
}

使用 try-with-resources 不僅使代碼變得通俗易懂,也更容易診斷。以firstLineOfFileAutoClose方法為例,如果調用 readLine()和 close() 方法都拋出異常,後一個異常就會被禁止,以保留第一個異常。(公號回復 高效 即可領取 Effective Java 第三版中文 pdf)

異常處理的原則

我們在日常處理異常的代碼中,應該遵循三個原則

不要捕獲類似 Exception 之類的異常,而應該捕獲類似特定的異常,比如 InterruptedException,方便排查問題,而且也能夠讓其他人接手你的代碼時,會減少罵你的次數。不要生吞異常。這是異常處理中要特別注重的事情。如果我們不把異常拋出來,或者也沒有輸出到 Logger 日誌中,程序可能會在後面以不可控的方式結束。不要在函數式編程中使用 checkedException。什麼是 Error

Error 是程序無法處理的錯誤,表示運行應用程式中較嚴重問題。大多數錯誤與代碼編寫者執行的操作無關,而表示代碼運行時 JVM(Java 虛擬機)出現的問題。這些錯誤是不可檢查的,因為它們在應用程式的控制和處理能力之 外,而且絕大多數是程序運行時不允許出現的狀況,比如 OutOfMemoryError 和 StackOverflowError異常的出現會有幾種情況,這裡需要先介紹一下 Java 內存模型 JDK1.7。

其中包括兩部分,由所有線程共享的數據區和線程隔離的數據區組成,在上面的 Java 內存模型中,只有程序計數器是不會發生 OutOfMemoryError 情況的區域,程序計數器控制著計算機指令的分支、循環、跳轉、異常處理和線程恢復,並且程序計數器是每個線程私有的。

什麼是線程私有:表示的就是各條線程之間互不影響,獨立存儲的內存區域。

如果應用程式執行的是 Java 方法,那麼這個計數器記錄的就是虛擬機字節碼指令的地址;如果正在執行的是 Native 方法,這個計數器值則為空(Undefined)。

除了程序計數器外,其他區域:方法區(Method Area)、虛擬機棧(VM Stack)、本地方法棧(Native Method Stack) 和 堆(Heap) 都是可能發生 OutOfMemoryError 的區域。

虛擬機棧:如果線程請求的棧深度大於虛擬機棧所允許的深度,將會出現 StackOverflowError 異常;如果虛擬機動態擴展無法申請到足夠的內存,將出現 OutOfMemoryError。

堆:Java 堆可以處於物理上不連續,邏輯上連續,就像我們的磁碟空間一樣,如果堆中沒有內存完成實例分配,並且堆無法擴展時,將會拋出 OutOfMemoryError。

方法區:方法區無法滿足內存分配需求時,將拋出 OutOfMemoryError 異常。

一道經典的面試題

一道非常經典的面試題,NoClassDefFoundError 和 ClassNotFoundException 有什麼區別

在類的加載過程中, JVM 或者 ClassLoader 無法找到對應的類時,都可能會引起這兩種異常/錯誤,由於不同的 ClassLoader 會從不同的地方加載類,有時是錯誤的 CLASSPATH 類路徑導致的這類錯誤,有時是某個庫的 jar 包缺失引發這類錯誤。NoClassDefFoundError 表示這個類在編譯時期存在,但是在運行時卻找不到此類,有時靜態初始化塊也會導致 NoClassDefFoundError 錯誤。

ClassLoader 是類路徑裝載器,在Java 中,類路徑裝載器一共有三種兩類

一種是虛擬機自帶的 ClassLoader,分為三種

啟動類加載器(Bootstrap) ,負責加載 $JAVAHOME/jre/lib/rt.jar擴展類加載器(Extension),負責加載 $JAVAHOME/jre/lib/ext/*.jar應用程式類加載器(AppClassLoader),加載當前應用的 classpath 的所有類

第二種是用戶自定義類加載器

Java.lang.ClassLoader 的子類,用戶可以定製類的加載方式。

另一方面,ClassNotFoundException 與編譯時期無關,當你嘗試在運行時使用反射加載類時,ClassNotFoundException 就會出現。

簡而言之,ClassNotFoundException 和 NoClassDefFoundError 都是由 CLASSPATH 中缺少類引起的,通常是由於缺少 JAR 文件而引起的,但是如果 JVM 認為應用運行時找不到相應的引用,就會拋出 NoClassDefFoundError 錯誤;當你在代碼中顯示的加載類比如 Class.forName() 調用時卻沒有找到相應的類,就會拋出 java.lang.ClassNotFoundException。

NoClassDefFoundError 是 JVM 引起的錯誤,是 unchecked,未經檢查的。因此不會使用 try-catch 或者 finally 語句塊;另外,ClassNotFoundException 是受檢異常,因此需要 try-catch 語句塊或者 try-finally 語句塊包圍,否則會導致編譯錯誤。調用 Class.forName()、ClassLoader.findClass() 和 ClassLoader.loadClass() 等方法時可能會引起 java.lang.ClassNotFoundException,如圖所示

NoClassDefFoundError 是連結錯誤,發生在連結階段,當解析引用找不到對應的類,就會觸發;而 ClassNotFoundException 是發生在運行時的異常。

文章參考:

https://www.java67.com/2012/12/noclassdeffounderror-vs-classnotfoundexception-java.html

《極客時間-Java核心技術 36 講》

《深入理解 Java 虛擬機》第二版

《Effective Java 第三版》

https://www.cnblogs.com/xiohao/p/3547443.html

https://blog.csdn.net/qq_29229567/article/details/80773970

https://blog.csdn.net/riemann_/article/details/87522352

《Java編程思想》

https://www.cnblogs.com/xz816111/p/8466048.html

https://docs.oracle.com/javase/specs/jls/se9/html/jls-11.html#jls-11.1.1

jdk 1.8 源碼注釋

嘿,你在看嗎

相關焦點

  • 面試官問我,使用Dubbo有沒有遇到一些坑?我笑了.
    前言17年的時候,因為一時衝動沒把持住(當然最近也有粉絲叫我再衝動一把再更新一波),結合面試題寫了一個系列的Dubbo源碼解析.目前公眾號大部分粉絲都是之前的粉絲,這裡不過多介紹.面試的時候,把源碼一波分析,令面試官虎軀一震!
  • 面試總是get不到面試官的問題點,該怎麼辦?
    產品經理面試,你GET到面試官的問題點了嗎?經常有同學在我們的B端產品交流群裡提問:面試被說沒有回答到點子上,不知道如何總結和表達?這個是每一個有過面試經驗的,或者說正在面試的同學都會遇到問題。因為你到了一個新的公司,所接觸的業務、面對的公司文化和團隊氛圍都可能不同,最能幫你快速適應和上手工作的就是底層思維和可遷移的能力。能理解面試官問題意圖的產品經理會怎麼回答這個問題呢?
  • 為什麼你的面試沒通過?可能和這3個雷區有關
    雖然是個節目,但在面試環節基本還是很真實的,不論是面試官的提問、考察點,還是求職者的回答。 今年職前菌繼續為大家剖析,希望看完這篇文章的童鞋們可以取TA們的精華,棄TA們的雷區。 職前菌將分2期,針對本季節目中候選人在面試中的表現,從雷區和可取之處2個方向展開分析。
  • 面試官:做個簡單的自我介紹!傻瓜說個沒完,聰明人只講這3點
    面試官:做個簡單的自我介紹!傻瓜說個沒完,聰明人只講這3點想要進入職場,第一關就是面試,如果你連這都過不去,那麼就更談不上什麼大展宏圖了。做人還是得腳踏實地,不能好高騖遠。有些人對面試這個環節並不怎麼注重,往往就會輸在細節上。人生,有的時候機會就只有那麼一次,一旦錯過就沒了,後悔莫及。所以,從最小的事情上做起,準備充分,這樣才能立於不敗之地。在面試的時候,相信很多朋友都會遇到面試官讓你做一個簡單的自我介紹這樣的問題。這種時候,就輪到你表現了,能不能被錄取,自我介紹佔了非常大的比重。
  • 《心動的offer》:李晉曄出場猶如教科書,面試官都說沒缺點
    《令人心動的offer》面試篇!《令人心動的offer》第二季播出了面試篇,但就是這麼一期節目,觀眾都忍不住自卑了,這到底是咋一回事呢?首先,《令人心動的offer》第二季節目依舊是圍繞了律協而展開的實習生涯,但初次面試的實習生一個比一個厲害,就比如王驍、詹秋怡等人,可當看到實習生李晉曄,我卻瞬間刷新了對學霸的認識!
  • 面試技巧 | 獵頭招聘經驗,面試官的刁鑽問題真的是故意為難嗎?
    為什麼那麼多人覺得面試很緊張,壓力很大呢?其實是因為很多面試官在面試的時候都會向求職者提出看似比較「刁鑽」問題,很多求職者就會覺得是面試官不想錄用他,在故意為難,其實事實並非如此。
  • 面試時,面試官提出跟工作無關的難題,面試官有何意圖?
    在面試中我們都會被面試官問到各種各樣的問題,有的是跟面試崗位有關的;有跟個人發展規劃有關的;有的是對公司規章制度方面的看法等等。有些面試官會提問一些跟工作無關的問題,甚至是刁難應聘著,其實這些問題也不是面試官故意用來刁難應聘者的,這些問題也是為了為自己的企業篩選人才而精心挑選出來的,自然有他們的考察目的。
  • 三個很常見的面試問題,背後都是面試官的陷阱,一不小心就會答錯
    1、請你先做個自我介紹這樣的問題你是不是覺得太平常了,大多人是脫口而出自己的年齡姓名、家庭出身地以及身高體重等非常基本的信息,這些都是在簡歷上能夠一眼看得到的。一旦面試官問了這樣的問題,其實不是想要聽你重複一遍已經有的信息。真正想要知道的是,你能否勝任這份工作。
  • 本文中主要對showlanguage與app中涉及的基礎問題進行介紹與剖析
    人機對話其實是計算機處理面對面問題的方式,程式設計師等人作為最高層最新版本的程序來處理面對面問題,怎麼想都覺得說多了也沒啥奇怪的,因為一個通用的api並不能很好的匹配問題,獲取有用的信息,因此人機對話在理解問題之前需要先對問題進行一定的理解,在本文中主要對showlanguage與app中涉及的基礎問題進行介紹與剖析。在此表示引用的作者均為筆者。
  • 面試官:和女上司出差只剩一間房,怎麼處理?男子機智回復被錄取
    求職面試不光考驗的是求職者的專業技術能力,更重要的是求職者處理問題的綜合能力,何為綜合能力,想必一千人心中有一千個哈姆雷特,但可以肯定的是面試官不會無緣無故的提問,每一個問題的背後都是在對求職者的考驗!
  • 面試官:你可以向我提出問題,年輕小夥巧問過面試,這3點需牢記
    在我看到過的一檔綜藝節目,跟實習相關的節目,裡面有一個令我拍手叫好的一個問題,一般面試當中,面試官都會在結尾讓你提出問題,有的是兩個,有的是一個,問題不在多少,在於怎麼問,他是這麼問的:」各位面試官希望在錄取的實習生中,最希望看到這名實習生能夠具備什麼樣的品質?「,正是因為這個問題,巧問過了面試。
  • 面試官:8斤酒你只有3斤和5斤的容器,怎麼平分?她一開口被錄取
    想要順利進入一家好的企業,首先就要通過一場面試。在面試的時候很多,面試官都會出一些比較奇葩這問題。看似與工作無關的問題其實是面試官對求職者情商和應變能力的考驗。小麗今年22歲,雖然是剛從大學畢業出來工作,但工作的經驗已經積累了非常之多了。
  • 面試官模仿《華爾街之狼》讓你「賣鋼筆」?不用怕,這篇文章為你全面支招!
    在銷售彥論第二季中,我們邀請到的嘉賓蔡宇就曾經提到:阿里考官在面試過程中,會隨手拿起身邊的一樣東西,讓面試候選人賣給自己」(阿里最強銷售鐵軍「軍營解密」【本期大咖:蔡宇】)。那麼,在面試中,遭遇這樣的奇葩提問,我們應該如何回答呢?這就需要回歸到這個問題設置的本質和目的。
  • 女面試官:「田」字加一筆是什麼字?答「甲」和「由」都慘遭淘汰
    大家都知道,在步入職場之前都會經歷過面試這一環節,然後在面試環節中,面試官不僅是要了解求職者的個人信息和專業能力,還要考察綜合素質。因此總會出一些奇葩的問題來考驗求職者的應用能力和反應能力。今天,小編就來和大家分享一家公司的面試官是如何用奇葩問題挑選員工的,一起來看看吧。
  • 青山學院+明治大學+立命館大學 面試全復盤!及考學高頻問題回答
    青山學院+明治大學+立命館大學 面試全復盤! 通過了校內考的洗禮,接下來,就是讓很多同學頭疼的面試了。我前前後後不知道面了多少次的試,還是很有資格來談談心得的。大家關心的無非就是會問什麼?該如何準備?這2個問題。
  • 聊聊作為面試官眼裡的FB面試玄學
    花時間整理了這一百多次面試中觀察到的一些規律,希望可以對準備申/面FB家的小夥伴們有些幫助。不講老生常談的面試準備,只講大家口裡的『玄學』。•先申明:純個人經驗,不代表所有的面試官/面試者都會有相同經歷。如果有現/前同事觀察到了和我所述矛盾的情況,希望也能分享出來避免大家被我誤導。
  • 面試官問你上家公司做什麼的,聰明人這樣回,被高看一眼
    面試官問你上家公司做什麼的,聰明人這樣回,被高看一眼面試是我們經常需要面對的一件事情,無論你是應屆畢業生還是已經在職場打拼的老油條,你想進入一個新的公司,都必須通過公司的面試。那麼我們就會遇到很多問題,面試官會在這個時候詢問我們一些問題,如果你回答得好,那麼你在面試官的心裡就會加分,如果你回答得不好,那麼你在面試官的心裡就會減分。那麼我們就來聊一聊,面試的時候你都會遇到哪些問題。有的時候,面試官想要了解你更多的東西,就會問你上家公司是做什麼的。有些人在被問到這個問題的時候,就會非常寬泛的回答。例如:我上家公司是做自媒體的。
  • 面試官:和異性出差,只剩一家旅館咋辦?求職者高情商回答被錄取
    小孫前往一家企業面試,面試官問了一個令人有些難堪的問題。可是這個所謂的表面上看起來有些難堪的問題,卻是職場當中經常會遇到的問題。今天就著這個機會,我們一起來看一下小孫前往這一家企業面試的過程當中,究竟提出了怎樣的問題,而小孫又是如何作答的呢?
  • 面試中的跳過那些坑——面試結尾篇
    今日分享你面試中的跳過那些坑---面試結尾篇 -在上一期的分享中我們介紹了面試技巧和注意事項天將降大任於斯人也,必先苦其心志,勞其筋骨,餓其體膚,空乏其身。請輸入用戶名:麥兜兒!請輸入密碼:一隻快樂的肥豬!
  • 央視面試被問:你敢不敢穿比基尼出境?楊瀾大方回答,折服面試官
    面試是邁入職場的第一步,也是每個人職場的第一課。面試不是簡簡單單地介紹自己,更重要的是向面試官展現自己的才能和潛力。很多人面試,在介紹完自己,就陷入了長久的沉默。只有當面試官問一句,他才會支支吾吾地應一聲。把自己的怯弱和緊張展現得淋漓盡致,至於能力方面,面試官完全沒辦法感覺到。於是,很自然地被淘汰了。工作難找,面試經常失敗,其大部分原因就是不知道該如何開口,如何回答面試官的問題,如何呈現自己的才能。