什么是異常
- 異常字面翻譯就是"意外"卦羡、"例外"的意思,也就是非正常情況.
- 異常本質(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及其子類描述各種不同的異常類型.
Throwable有兩個(gè)重要的子類:Exception和Error
1.ErrorError是程序無法處理的錯(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.ExceptionException是程序本身可以處理的異常.異常處理通常指針對這種類型異常的處理.
Exception類的異常包括checked exception和unchecked exception
2.1.unchecked exceptionunchecked 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
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.常見的異常類型
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ò)誤的
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異常了:
那么這種異常通常是如何產(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 | 線程中斷 |