JAVA:異常

什么是異常

  • 異常字面翻譯就是"意外"卦羡、"例外"的意思,也就是非正常情況.
  • 異常本質(zhì)上是程序上的錯(cuò)誤.

程序中的異常

  • 錯(cuò)誤在我們編寫程序的過程中會(huì)經(jīng)常發(fā)生,包括編譯期間和運(yùn)行期間的錯(cuò)誤.
  • 在編譯期間出現(xiàn)的錯(cuò)誤有編譯器幫助我們一起修正,然而運(yùn)行期間的錯(cuò)誤便不是編譯器力所能及的了,并且運(yùn)行期間的錯(cuò)誤往往是難以預(yù)料的.
  • 在程序運(yùn)行過程中,意外發(fā)生的情況,背離我們程序本身的意圖的表現(xiàn),都可以理解為異常.
  • 當(dāng)程序在運(yùn)行期間出現(xiàn)了異常,如果置之不理,程序可能會(huì)不正常運(yùn)行、強(qiáng)制中斷運(yùn)行、造成用戶數(shù)據(jù)丟失擎宝、資源無法正常釋放郁惜、直接導(dǎo)致系統(tǒng)崩潰,顯然這不是我們希望看到的結(jié)果.
  • Java提供了異常機(jī)制來進(jìn)行處理,通過異常機(jī)制,我們可以更好地提升程序的健壯性.

異常的分類

  • 在程序開發(fā)中,異常指不期而至的各種狀況.它是一個(gè)事件,當(dāng)發(fā)生在程序運(yùn)行期間時(shí),會(huì)干擾正常的指令流程.

  • 在Java中,通過Throwable及其子類描述各種不同的異常類型.


    異常分類.png
  • Throwable有兩個(gè)重要的子類:Exception和Error
    1.Error

  • Error是程序無法處理的錯(cuò)誤,表示運(yùn)行應(yīng)用程序中較嚴(yán)重問題.大多數(shù)錯(cuò)誤與代碼編寫者執(zhí)行的操作無關(guān),而表示代碼運(yùn)行時(shí)JVM(Java虛擬機(jī))出現(xiàn)的問題.

  • 例如,Java虛擬機(jī)運(yùn)行錯(cuò)誤(Virtual MachineError),當(dāng)JVM不再有繼續(xù)執(zhí)行操作所需的內(nèi)存資源時(shí),將出現(xiàn)OutOfMemoryError

  • 這些錯(cuò)誤是不可查的,因?yàn)樗鼈冊趹?yīng)用程序的控制和處理能力之外,而且絕大多數(shù)是程序運(yùn)行時(shí)不允許出現(xiàn)的狀況.

  • 對于設(shè)計(jì)合理的應(yīng)用程序來說,即使確實(shí)發(fā)生了錯(cuò)誤,本質(zhì)上也不應(yīng)該試圖去處理它所引起的異常狀況.

  • 因此我們編寫程序時(shí)不需要關(guān)心這類異常.
    2.Exception

  • Exception是程序本身可以處理的異常.異常處理通常指針對這種類型異常的處理.

  • Exception類的異常包括checked exception和unchecked exception
    2.1.unchecked exception

  • unchecked exception:編譯器不要求強(qiáng)制處置的異常.

  • 包含RuntimeException類及其子類異常.

  • 如NullPointerException(空指針異常)水评、IndexOutOfBoundsException(下標(biāo)越界異常)等,這些異常時(shí)unchecked exception.

  • Java編譯器不會(huì)檢查這些異常,在程序中可以選擇捕獲處理,也可以不處理,照樣正常編譯通過.
    2.2. checked exception

  • 編譯器要求必須處置的異常.

  • 是RuntimeException及其子類以外,其他的Exception類的子類.

  • 如IOException猩系、SQLException等
    3.異常處理

  • Java編譯器會(huì)檢查這些異常,當(dāng)程序中可能出現(xiàn)這類異常時(shí),要求必須進(jìn)行異常處理,否則編譯不會(huì)通過.

  • 在Java應(yīng)用程序中,異常處理機(jī)制為:拋出異常、捕獲異常
    4.拋出異常

  • 當(dāng)一個(gè)方法出現(xiàn)錯(cuò)誤引發(fā)異常時(shí),方法創(chuàng)建異常對象并交付運(yùn)行時(shí)系統(tǒng).

  • 異常對象中包含了異常類型和異常出現(xiàn)時(shí)的程序狀態(tài)等異常信息.

  • 運(yùn)行時(shí)系統(tǒng)負(fù)責(zé)尋找處置異常的代碼并執(zhí)行.
    5.捕獲異常

  • 在方法拋出異常之后,運(yùn)行時(shí)系統(tǒng)將轉(zhuǎn)為尋找合適的異常處理器.

  • 運(yùn)行時(shí)系統(tǒng)從發(fā)生異常的方法開始,一次回查調(diào)用棧中的方法,當(dāng)異常處理器所處理的異常類型與方法拋出的異常類型相符時(shí),即為合適的異常處理器.

  • 當(dāng)運(yùn)行時(shí)系統(tǒng)遍歷調(diào)用棧而未找到合適的異常處理器,則運(yùn)行時(shí)系統(tǒng)終止.同時(shí),意味著Java程序的終止.

  • 對于運(yùn)行時(shí)異常中燥、錯(cuò)誤或可查異常,Java技術(shù)所要求的的異常處理方式有所不同.

  • 總體來說,Java規(guī)定:對于可查異常必須捕捉寇甸、或者聲明拋出.允許忽略不可查的RuntimeException和Error.

  • 簡單地說,異常總是先被拋出,后被捕捉的.
    6.異常處理

  • 通過5個(gè)關(guān)鍵字來實(shí)現(xiàn):try疗涉、catch拿霉、finally、throw咱扣、throws

    異常處理.png

    7.try-catch-finally

  public void method(){
    try{
          // 代碼段1
          // 產(chǎn)生異常的代碼段2
    }catch(異常類型ex){
        // 對異常進(jìn)行處理的代碼段3
    }finally{
        // 代碼段4
    }
}

8.使用try-catch塊捕獲并且處理異常

  public void method(){
    try{
        // 代碼段
    }catch(異常類型ex){
      // 對異常進(jìn)行處理的代碼段
    }
  }

9.多重catch塊

  • 引發(fā)多種類型的異常
    -排列catch語句的順序:先子類后父類
    -發(fā)生異常時(shí)按順序逐個(gè)匹配
    -只執(zhí)行第一個(gè)與異常類型匹配的catch語句
  public void method(){
  try{
      // 代碼段
      // 產(chǎn)生異常 
  }catch(異常類型1 ex){
    // 對異常進(jìn)行處理的代碼段
  }catch(異常類型2 ex){
    // 對異常進(jìn)行處理的代碼段
  }catch(異常類型3 ex){
    // 對異常進(jìn)行處理的代碼段
  }
// 代碼段
}

10.try-catch-finally

  • try塊后可以接零個(gè)或多個(gè)catch塊
  • 如果沒有catch,則必須跟一個(gè)finally塊
  • catch绽淘、finally可選
  • 語法組合:
    -try-catch
    -try-finally
    -try-catch-finally
    -try-catch-catch-finally
  • 在try-catch塊后加入finally塊
    -是否發(fā)生異常都只需
    -不執(zhí)行的唯一情況:無異常,中斷程序,退出Java虛擬機(jī)
    實(shí)際應(yīng)用中的經(jīng)驗(yàn)總結(jié)
    * 處理運(yùn)行時(shí)異常時(shí),采用邏輯去合理規(guī)避同時(shí)輔助try-catch處理
    * 在多重catch塊后面,可以加一個(gè)catch(Exception)來處理可能會(huì)被遺漏的異常
    * 對于不明確的代碼,也可以加上try-catch,處理潛在的異常
    * 盡量去處理異常,切記只是簡單地調(diào)用printStackTrace()去打印輸出
    * 具體如何處理異常,要根據(jù)不同的業(yè)務(wù)需求和異常類型去決定
    * 盡量添加finally語句塊去釋放占用的資源
public void method(){
  try{
      // 代碼段1
      // 產(chǎn)生異常的代碼段2
  }catch(異常類型ex){
    // 對異常進(jìn)行處理的代碼段3
    return;
  } finally{
  // 代碼段4
  }
}

11.常見的異常類型

常見異常類型.png

12.throw & throws

  • 可以通過throws聲明將要拋出何種類型的異常,通過throw將產(chǎn)生的異常拋出.
    12.1.throws
  • 如果一個(gè)方法可能會(huì)出現(xiàn)異常,但沒有能力處理這種異常,可以在方法聲明處用throws子句來聲明拋出異常.
  • 例如:汽車在運(yùn)行時(shí)可能會(huì)出現(xiàn)故障,汽車本身沒辦法處理這個(gè)故障,那就讓開車的人來處理.
  • throws語句用在方法定義時(shí)聲明該方法要拋出的異常類型.
    public void method()throws Exception1,Exception2,...,ExceptionN {
    // 可能產(chǎn)生異常的代碼
}
  • 當(dāng)方法拋出異常列表中的異常時(shí),方法將不對這些類型及其子類類型的異常作處理,而拋向調(diào)用該方法的方法,由他去處理.
    throws的使用規(guī)則:
  • 1、如果是不可查異常(unchecked exception),即Error偏窝、RuntimeException或它們的子類,那么可以不使用throws關(guān)鍵字來聲明要拋出的異常,編譯仍能順利通過,但在運(yùn)行時(shí)會(huì)被系統(tǒng)拋出.
  • 2收恢、如果一個(gè)方法中可能出現(xiàn)可查異常,要么用try-catch語句捕獲,要么用throws子句聲明將它拋出,否則會(huì)導(dǎo)致編譯錯(cuò)誤.
  • 3、當(dāng)拋出了異常,則該方法的調(diào)用者必須處理或者重新拋出該異常類.
  • 4祭往、當(dāng)子類重寫父類拋出異常的方法時(shí),聲明的異常必須是父類方法所聲明異常的同類或子類.
    12.2throw
  • throw用來拋出一個(gè)異常
    例如:throw new IOException();
  • throw拋出的只能夠是可拋出類Throwable或者其子類的實(shí)例對象.
    例如:throw new String("出錯(cuò)啦");是錯(cuò)誤的


    throw.png

13.自定義異常

  • 使用Java內(nèi)置的異常類可以描述在編程時(shí)出現(xiàn)的大部分異常情況.
  • 也可以通過自定義異常描述特定業(yè)務(wù)產(chǎn)生的異常類型
  • 所謂自定義異常,就是定義一個(gè)類,去繼承Throwable類或者它的子類.
    ?
    14.異常鏈
  • 有時(shí)候我們會(huì)捕獲一個(gè)異常后再拋出另一個(gè)異常
  • 顧名思義就是:將異常發(fā)生的原因一個(gè)傳一個(gè)串起來,即把底層的異常信息傳個(gè)上層,這樣逐層拋出

15.Java常見異常類型及原因分析
一.NullPointerException異常
?顧名思義,NullPointerException是空指針異常.但是在Java中沒有指針,怎么會(huì)有空指針異常呢?
?在C++中,聲明的指針需要指向一個(gè)實(shí)例(通過new方法構(gòu)造),這個(gè)指針可以理解為地址.在Java中,雖然沒有指針,但是有引用(通常稱為對象引用,一般直接說對象),引用也是需要指向一個(gè)實(shí)例對象(通過new方法構(gòu)造)的,從這種意義上說,Java中的引用與C++中的指針沒有本質(zhì)的區(qū)別,不同的是,處于安全的目的,在Java中不能對引用進(jìn)行操作,而在C++中可以直接進(jìn)行指針的運(yùn)算,例如book++等.
?所以這里的NullPointerException雖然不是真正的空指針異常,但本質(zhì)上差不多,是引用沒有指向具體的實(shí)例,所以當(dāng)訪問這個(gè)引用的方法的時(shí)候就會(huì)產(chǎn)生這種異常.例如下面的代碼:

 String  str = "這是衣蛾測試的字符串!";
System.out.println(str.length());
// 這段代碼沒有問題,如果改成下面的代碼:
 String str = null;
 System.out.println(str.length());
// 就會(huì)產(chǎn)生NullPointerException異常了:

\color{red}{Exception in thread "main"}\color{blue}{java.lang.NullPointerException } \color{red}{ at com.diandian.test.Test.main(}\color{blue}{Test.java:35}\color{red}{)}
那么這種異常通常是如何產(chǎn)生的呢?比較多見的是下面的兩種情況:
a) 把調(diào)用某個(gè)方法的返回值直接賦值給某個(gè)引用,然后調(diào)用這個(gè)引用的方法.在這種情況下,如果返回的值是null,必然會(huì)產(chǎn)生NullPointerException異常.
例如:

// 聲明一個(gè)People類,并打印出該對象中的Name值.
public static void main(String[] args){
  People p = null;
  p.setName("張三");
  System.out.println(p.getName());
}

說明:這個(gè)時(shí)候p就會(huì)出現(xiàn)空指針異常,因?yàn)檫@里只是聲明了這個(gè)People類型的對象并沒有創(chuàng)建對象,所以它的堆里面沒有地址引用,切忌你喲啊用對象調(diào)用方法的時(shí)候一定要先創(chuàng)建對象.
b) 在方法體中調(diào)用參數(shù)的方法
?這種情況下,如果調(diào)用方法的時(shí)候傳遞進(jìn)來的值是null,也會(huì)產(chǎn)生NullPointerException異常.要解決這種異常,只需要檢查異常出現(xiàn)在第幾行(通常在集成開發(fā)環(huán)境中會(huì)提示用戶錯(cuò)誤發(fā)生在第幾行),然后查看調(diào)用了哪個(gè)對象的方法,然后檢查這個(gè)對象為什么沒有賦值成功即可.
?要避免程序產(chǎn)生這種異常,比較好的解決方法時(shí)在調(diào)用某個(gè)對象的方法時(shí)候判斷這個(gè)對象是否為空,如果可能,則增加判斷語句,例如上面的代碼可以寫成:

  if(str!=null){
  System.out.println(str.length());
}else{
  System.out.println(0);
}

二.ClassCastException異常
?從字面上看,是類型轉(zhuǎn)換錯(cuò)誤,通常是進(jìn)行強(qiáng)制類型轉(zhuǎn)換時(shí)候出的錯(cuò)誤.
這種異常時(shí)如何產(chǎn)生的呢?舉個(gè)例子.

// Animal 表示動(dòng)物,Dog表示狗,是動(dòng)物的子類,Cat表示貓,是動(dòng)物的子類.
Animal a1 = new Dog();
Animal a2 = new Cat();
Dog d1 = (Dog)a1;
Dog d2 = (Dog)a2;

?第三行和第四行代碼基本相同,從字面意思看都是動(dòng)物(Animal)強(qiáng)制轉(zhuǎn)換為狗(dog).但是第四行代碼將產(chǎn)生java.lang.ClassCastException.原因是把一個(gè)Cat轉(zhuǎn)換成Dog不可以,而第三行中是把Dog轉(zhuǎn)換成Dog,所以可以.
?從上面的例子看,java.lang.ClassCastException是進(jìn)行強(qiáng)制類型轉(zhuǎn)換的時(shí)候產(chǎn)生的異常,
?強(qiáng)制類型轉(zhuǎn)換的前提是父類引用指向的對象的類型是子類的時(shí)候才可以進(jìn)行強(qiáng)制類型轉(zhuǎn)換,如果父類引用指向的對象的類型不是子類的時(shí)候?qū)a(chǎn)生java.lang.ClassCastException異常.
?遇到這樣的異常的時(shí)候如何解決呢?如果你知道要訪問的對象的具體類型,直接轉(zhuǎn)換成該類型即可.如果不能確定類型可以通過下面的兩種方式進(jìn)行處理(假設(shè)對象為o):

  • 通過o.getClass().getName()得到具體的類型,可以通過輸出語句輸出這個(gè)類型,然后根據(jù)類型進(jìn)行具體的處理
  • 通過if(o instanceof 類型)的語句來判斷o的類型是什么.
    三.ArrayIndexOutOfBoundsException 異常
    ?這是一個(gè)非常常見的異常,從名字上看是數(shù)組下標(biāo)越界錯(cuò)誤,解決方法就是查看為什么下標(biāo)越界,下面是一個(gè)錯(cuò)誤示例:
    Exception in thread "main" java.lang.ArrayIndexOutOfBoundsExcption:2at test4.nextStates
    (State.java:93)at test4.State.main(State.java:478)
    ?從這些提示信息中可以獲取如下信息:
    1)錯(cuò)誤發(fā)生在93行,
    2)發(fā)生錯(cuò)誤的時(shí)候,下標(biāo)的值為2
    接下來分析為什么下標(biāo)值是2為什么不可以就可以解決了.
    四.UnsupportedClassVersionError
    錯(cuò)誤提示如下:
    java.lang.UnsupportedClassVersionError:Bad version number in .class file
    錯(cuò)誤原因:
    ?編譯Java和運(yùn)行Java所使用的的Java的版本不一致.例如,編譯的時(shí)候使用的Java版本是6,運(yùn)行時(shí)候使用的Java版本是5.
    解決方案:修改運(yùn)行環(huán)境的Java版本或者修改編譯環(huán)境的Java版本,讓兩者保持一致.
    五.NumberFormatExcption異常
    ?數(shù)字轉(zhuǎn)換異常,在把一個(gè)表示數(shù)字的字符串轉(zhuǎn)換成數(shù)字類型的時(shí)候可能會(huì)報(bào)這個(gè)異常,原因是作為參數(shù)的字符串不是有數(shù)字組成的.
    六.堆棧溢出和內(nèi)存溢出
    ?在遞歸調(diào)用的時(shí)候可能會(huì)產(chǎn)生堆棧溢出的情況,因?yàn)樵谶f歸調(diào)用的時(shí)候需要把調(diào)用的狀態(tài)保存起來,如果遞歸的深度達(dá)到一定程度,將產(chǎn)生堆棧溢出的異常.
    ?如果虛擬機(jī)的內(nèi)存表較小,而程序?qū)?nèi)存的要求比較高,則可能產(chǎn)生內(nèi)存溢出錯(cuò)誤.

常見異常類

異常 說明
RuntimeException Java.lang包中多數(shù)異常的基類
ArithmeticException 算術(shù)錯(cuò)誤,如除以0
IIIegalArgumentException 方法收到非法參數(shù)
SecurityException 試圖違反安全性
ClassNotFoundException 不能加載請求的類
AWTException AWT中的異常
IOException I/O異常的根類
FileNotFoundException 不能找到文件
EOFException 文件結(jié)束
IIIegalAccessException 對類的訪問被拒絕
NoSuchMethodException 請求的方法不存在
InterruptedException 線程中斷
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末伦意,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子硼补,更是在濱河造成了極大的恐慌驮肉,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,029評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件已骇,死亡現(xiàn)場離奇詭異离钝,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)褪储,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,395評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門卵渴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人鲤竹,你說我怎么就攤上這事浪读。” “怎么了辛藻?”我有些...
    開封第一講書人閱讀 157,570評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵碘橘,是天一觀的道長。 經(jīng)常有香客問我吱肌,道長痘拆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,535評(píng)論 1 284
  • 正文 為了忘掉前任氮墨,我火速辦了婚禮纺蛆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘勇边。我一直安慰自己犹撒,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,650評(píng)論 6 386
  • 文/花漫 我一把揭開白布粒褒。 她就那樣靜靜地躺著识颊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪奕坟。 梳的紋絲不亂的頭發(fā)上祥款,一...
    開封第一講書人閱讀 49,850評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音月杉,去河邊找鬼刃跛。 笑死,一個(gè)胖子當(dāng)著我的面吹牛苛萎,可吹牛的內(nèi)容都是我干的桨昙。 我是一名探鬼主播检号,決...
    沈念sama閱讀 39,006評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蛙酪!你這毒婦竟也來了齐苛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,747評(píng)論 0 268
  • 序言:老撾萬榮一對情侶失蹤桂塞,失蹤者是張志新(化名)和其女友劉穎凹蜂,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體阁危,經(jīng)...
    沈念sama閱讀 44,207評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡玛痊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,536評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了狂打。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片擂煞。...
    茶點(diǎn)故事閱讀 38,683評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖趴乡,靈堂內(nèi)的尸體忽然破棺而出颈娜,到底是詐尸還是另有隱情,我是刑警寧澤浙宜,帶...
    沈念sama閱讀 34,342評(píng)論 4 330
  • 正文 年R本政府宣布官辽,位于F島的核電站,受9級(jí)特大地震影響粟瞬,放射性物質(zhì)發(fā)生泄漏同仆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,964評(píng)論 3 315
  • 文/蒙蒙 一裙品、第九天 我趴在偏房一處隱蔽的房頂上張望俗批。 院中可真熱鬧,春花似錦市怎、人聲如沸岁忘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,772評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽干像。三九已至,卻和暖如春驰弄,著一層夾襖步出監(jiān)牢的瞬間麻汰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,004評(píng)論 1 266
  • 我被黑心中介騙來泰國打工戚篙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留五鲫,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,401評(píng)論 2 360
  • 正文 我出身青樓岔擂,卻偏偏與公主長得像位喂,于是被迫代替她去往敵國和親浪耘。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,566評(píng)論 2 349