Java超詳細(xì)梳理異常處理機(jī)制
一、異常概述與異常體系結(jié)構(gòu)
1. 異常概述
引入
在使用計(jì)算機(jī)語(yǔ)言進(jìn)行項(xiàng)目開(kāi)發(fā)的過(guò)程中,即使程序員把代碼寫(xiě)得盡善盡美,在系統(tǒng)的運(yùn)行過(guò)程中仍然會(huì)遇到一些問(wèn)題,因?yàn)楹芏鄦?wèn)題不是靠代碼能夠避免的,比如:客戶輸入數(shù)據(jù)的格式,讀取文件是否存在,網(wǎng)絡(luò)是否始終保持通暢等等。
概念
在Java語(yǔ)言中,將程序執(zhí)行中發(fā)生的不正常情況稱(chēng)為 “ 異常 ”。 (開(kāi)發(fā)過(guò)程中的語(yǔ)法錯(cuò)誤和邏輯錯(cuò)誤不是異常)
2. 分類(lèi)
2.1 Error vs Exception
Java程序在執(zhí)行過(guò)程中所發(fā)生的異常事件可分為兩類(lèi):
(1) Error
Java虛擬機(jī)無(wú)法解決的嚴(yán)重問(wèn)題。如:JVM系統(tǒng)內(nèi)部錯(cuò)誤、資源耗盡等嚴(yán)重情況。比如:StackOverflowError和OOM。一般不編寫(xiě)針對(duì)性的代碼進(jìn)行處理。
(2)Exception
其它因編程錯(cuò)誤或偶然的外在因素導(dǎo)致的一般性問(wèn)題,可以使用針對(duì)性的代碼進(jìn)行處理。例如:
| 常見(jiàn)Exception |
|---|
| 空指針訪問(wèn) |
| 試圖讀取不存在的文件 |
| 網(wǎng)絡(luò)連接中斷 |
| 數(shù)組角標(biāo)越界 |
對(duì)于這些錯(cuò)誤,一般有兩種解決方法:
- 遇到錯(cuò)誤就終止程序的運(yùn)行。
- 由程序員在編寫(xiě)程序時(shí),就考慮到錯(cuò)誤的檢測(cè)、錯(cuò)誤消息的提示,以及錯(cuò)誤的處理。
注意:程序員通常只能處理Exception,而對(duì)Error無(wú)能為力。
2.2 編譯時(shí)異常vs運(yùn)行時(shí)異常
分類(lèi):編譯時(shí)異常和運(yùn)行時(shí)異常
(1)編譯時(shí)異常
編譯時(shí)異常是指編譯器要求必須處置的異常。即程序在運(yùn)行時(shí)由于外界因素造成的一般性異常。編譯器要求Java程序必須捕獲或聲明所有編譯時(shí)異常。
對(duì)于這類(lèi)異常,如果程序不處理,可能會(huì)帶來(lái)意想不到的結(jié)果。
舉例:
IOException(FileNotFoundException)ClassNotFoundException
(2)運(yùn)行時(shí)異常
運(yùn)行時(shí)異常是指編譯器不要求強(qiáng)制處置的異常。一般是指編程時(shí)的邏輯錯(cuò)誤,是程序員應(yīng)該積極避免其出現(xiàn)的異常。java.lang.RuntimeException類(lèi)及它的子類(lèi)都是運(yùn)行時(shí)異常。
對(duì)于這類(lèi)異常,可以不作處理,因?yàn)檫@類(lèi)異常很普遍,若全處理可能會(huì)對(duì)程序的可讀性和運(yùn)行效率產(chǎn)生影響。
舉例:
NullPointerExceptionArrayIndexOutOfBoundsExceptionClassCastExceptionNumberFormatExceptionInputMismatchExceptionArithmeticException
注意: 捕獲錯(cuò)誤最理想的是在編譯期間,但有的錯(cuò)誤只有在運(yùn)行時(shí)才會(huì)發(fā)生。
3. 常見(jiàn)異常
3.1 分類(lèi)
(1) java.lang.RuntimeException
ClassCastExceptionArrayIndexOutOfBoundsExceptionNullPointerExceptionArithmeticExceptionNumberFormatExceptionInputMismatchException- …
(2) java.io.IOExeption
FileNotFoundExceptionEOFException
(3) java.lang.ClassNotFoundException
(4) java.lang.InterruptedException
(5) java.io.FileNotFoundException
(6) java.sql.SQLException
3.2 代碼演示
ClassCastException :
public class Order {
public static void main(String[] args) {
Object obj = new Date();
Order order;
order = (Order) obj;
System.out.println(order);
}
}

ArrayIndexOutOfBoundsException :
public class IndexOutExp {
public static void main(String[] args) {
String friends[] = { "lisa", "bily", "kessy" };
for (int i = 0; i < 5; i++) {
System.out.println(friends[i]); // friends[4]?
}
System.out.println("\nthis is the end");
}
}

NullPointerException :
public class NullRef {
int i = 1;
public static void main(String[] args) {
NullRef t = new NullRef();
t = null;
System.out.println(t.i);
}
}
ArithmeticException :
public class DivideZero {
int x;
public static void main(String[] args) {
int y;
DivideZero c=new DivideZero();
y=3/c.x;
System.out.println("program ends ok!");
}
}

NumberFormatException :
public void test1(){
String str = "123";
str = "abc";
int num = Integer.parseInt(str);
}

InputMismatchException:
public void test1(){
Scanner scanner = new Scanner(System.in);
int score = scanner.nextInt();
System.out.println(score);
scanner.close();
}
二、異常處理機(jī)制
1. 概述
(1)在編寫(xiě)程序時(shí),經(jīng)常要在可能出現(xiàn)錯(cuò)誤的地方加上檢測(cè)的代碼。
如進(jìn)行x/y運(yùn)算時(shí),要檢測(cè)分母為0,數(shù)據(jù)為空,輸入的不是數(shù)據(jù),而是字符等。過(guò)多的if-else分支會(huì)導(dǎo)致程序的代碼加長(zhǎng)、臃腫、可讀性差。因此采用異常處理機(jī)制。
(2)Java采用的異常處理機(jī)制,是將異常處理的程序代碼集中在一起,與正常的程序代碼分開(kāi),使得程序簡(jiǎn)潔、優(yōu)雅,并易于維護(hù)。
(3)Java提供的是異常處理的抓拋模型。
Java程序的執(zhí)行過(guò)程中如出現(xiàn)異常,會(huì)生成一個(gè)異常類(lèi)對(duì)象,該異常對(duì)象將被提交給Java運(yùn)行時(shí)系統(tǒng),這個(gè)過(guò)程稱(chēng)為拋出(throw)異常。
過(guò)程一:“拋”:程序在正常執(zhí)行的過(guò)程中,一旦出現(xiàn)異常,就會(huì)在異常代碼處生成一個(gè)對(duì)應(yīng)異常類(lèi)的對(duì)象。并將此對(duì)象拋出。一旦拋出對(duì)象以后,其后的代碼就不再執(zhí)行。
過(guò)程二:“抓”:可以理解為異常的處理方式:
try-catch-finallythrows
(4)異常對(duì)象的生成 :
首先要生成異常類(lèi)對(duì)象,然后通過(guò)throw語(yǔ)句實(shí)現(xiàn)拋出操作(提交給Java運(yùn)行環(huán)境)。
- 由虛擬機(jī)自動(dòng)生成:
程序運(yùn)行過(guò)程中,虛擬機(jī)檢測(cè)到程序發(fā)生了問(wèn)題,如果在當(dāng)前代碼中沒(méi)有找到相應(yīng)的處理程序,就會(huì)在后臺(tái)自動(dòng)創(chuàng)建一個(gè)對(duì)應(yīng)異常類(lèi)的實(shí)例對(duì)象并拋出——自動(dòng)拋出
- 由開(kāi)發(fā)人員手動(dòng)創(chuàng)建:
Exception exception = new ClassCastException();——創(chuàng)建好的異常對(duì)象不拋出對(duì)程序沒(méi)有任何影響,和創(chuàng)建一個(gè)普通對(duì)象一樣。
2. 異常處理機(jī)制一之try-catch-finally
2.1 語(yǔ)法格式
try{
...... //可能產(chǎn)生異常的代碼
}
catch( ExceptionName1 e ){
...... //當(dāng)產(chǎn)生ExceptionName1型異常時(shí)的處置措施
}
catch( ExceptionName2 e ){
...... //當(dāng)產(chǎn)生ExceptionName2型異常時(shí)的處置措施
} finally{
...... //無(wú)論是否發(fā)生異常,都無(wú)條件執(zhí)行的語(yǔ)句
}
2.2 使用
(1)try
① 捕獲異常的第一步是用try{…}語(yǔ)句塊選定捕獲異常的范圍,將可能出現(xiàn)異常的代碼放在try語(yǔ)句塊中。
② 在執(zhí)行過(guò)程中,一旦出現(xiàn)異常,就會(huì)生成一個(gè)對(duì)應(yīng)異常類(lèi)的對(duì)象,根據(jù)此對(duì)象的類(lèi)型,去catch中進(jìn)行匹配。
③ 一旦try中的異常對(duì)象匹配到某一個(gè)catch時(shí),就進(jìn)入catch中進(jìn)行異常的處理。
④ 一旦處理完成,就跳出當(dāng)前的 try-catch結(jié)構(gòu)(在沒(méi)有寫(xiě)finally的情況),繼續(xù)執(zhí)行其后的代碼。
以上執(zhí)行步驟順序:① ==> ② ==> ③ ==> ④
在try結(jié)構(gòu)中聲明的變量,再出了try結(jié)構(gòu)以后,就不能再被調(diào)用。
try-catch-finally結(jié)構(gòu)可以嵌套。
(2)catch (Exceptiontype e)
- 在
catch語(yǔ)句塊中是對(duì)異常對(duì)象進(jìn)行處理的代碼。 - 每個(gè)
try語(yǔ)句塊可以伴隨一個(gè)或多個(gè)catch語(yǔ)句,用于處理可能產(chǎn)生的不同類(lèi)型的異常對(duì)象。 - 如果明確知道產(chǎn)生的是何種異常,可以用該異常類(lèi)作為
catch的參數(shù),也可以用其父類(lèi)作為catch的參數(shù)。 catch中的異常類(lèi)型如果滿足子父類(lèi)關(guān)系,則要求子類(lèi)一定聲明在父類(lèi)的上面。否則,報(bào)錯(cuò)。catch中的異常類(lèi)型如果沒(méi)有滿足子父類(lèi)關(guān)系,則誰(shuí)聲明在上,誰(shuí)聲明在下無(wú)所謂。比如:可以用ArithmeticException類(lèi)作為參數(shù)的地方,就可以用RuntimeException類(lèi)作為參數(shù),或者用所有異常的父類(lèi)Exception類(lèi)作為參數(shù)。但不能是與ArithmeticException類(lèi)無(wú)關(guān)的異常,如NullPointerException(catch中的語(yǔ)句將不會(huì)執(zhí)行)。
(3)捕獲異常的有關(guān)信息(寫(xiě)在catch{ }語(yǔ)句中):
與其它對(duì)象一樣,可以訪問(wèn)一個(gè)異常對(duì)象的成員變量或調(diào)用它的方法。
getMessage()獲取異常信息,返回字符串 。printStackTrace()獲取異常類(lèi)名和異常信息,以及異常出 現(xiàn)在程序中的位置。返回值void。

(4)finally
finally語(yǔ)句和catch語(yǔ)句是任選的。- 不論在
try代碼塊中是否發(fā)生了異常事件,catch語(yǔ)句是否執(zhí)行,catch語(yǔ)句是否有異常,catch語(yǔ)句中是否有return,finally塊中的語(yǔ)句都會(huì)被執(zhí)行。 - 捕獲異常的最后一步是通過(guò)
finally語(yǔ)句為異常處理提供一個(gè)統(tǒng)一的出口,使得在控制流轉(zhuǎn)到程序的其它部分以前,能夠?qū)Τ绦虻臓顟B(tài)作統(tǒng)一的管理。

2.3 代碼演示
//舉例一:
/*
例子一使用的異常都是RuntimeException類(lèi)或是它的子類(lèi),這些類(lèi)的異常的特點(diǎn)是:
即使沒(méi)有使用try和catch捕獲,Java自己也能捕獲,并且編譯通過(guò)(但運(yùn)行時(shí)會(huì)發(fā)生異常使得程序運(yùn)行終止)。
*/
public class IndexOutExp {
public static void main(String[] args) {
String friends[] = { "lisa", "bily", "kessy" };
try {
for (int i = 0; i < 5; i++) {
System.out.println(friends[i]);
}
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("index err");
}
System.out.println("\nthis is the end");
}
}
/* output:
lisa
bily
kessy
index err
this is the end
*/
//舉例二:
/*
如果拋出的異常是IOException等類(lèi)型的非運(yùn)行時(shí)異常,則必須捕獲,否則
編譯錯(cuò)誤。也就是說(shuō),我們必須處理編譯時(shí)異常,將異常進(jìn)行捕捉,轉(zhuǎn)化為
運(yùn)行時(shí)異常。
*/
public class IOExp {
public static void main(String[] args) {
FileInputStream in = new FileInputStream("atguigushk.txt");
int b;
b = in.read();
while (b != -1) {
System.out.print((char) b);
b = in.read();
}
in.close();
}
}
3. 異常處理機(jī)制二之聲明拋出異常throws
3.1 語(yǔ)法格式
"throws + 異常類(lèi)型"寫(xiě)在方法的聲明處。指明此方法執(zhí)行時(shí),可能會(huì)拋出的異常類(lèi)型。
public void readFile(String file) throws FileNotFoundException {
……
// 讀文件的操作可能產(chǎn)生FileNotFoundException類(lèi)型的異常
FileInputStream fis = new FileInputStream(file);
……
}
3.2 使用
(1)聲明拋出異常是Java中處理異常的第二種方式
(2)如果一個(gè)方法(中的語(yǔ)句執(zhí)行時(shí))可能生成某種異常,但是并不能確定如何處理這種異常,則此方法應(yīng)顯示地聲明拋出異常,表明該方法將不對(duì)這些異常進(jìn)行處理,而由該方法的調(diào)用者負(fù)責(zé)處理。
(3)在方法聲明中用throws語(yǔ)句可以聲明拋出異常的列表,throws后面的異常類(lèi)型可以是方法中產(chǎn)生的異常類(lèi)型,也可以是它的父類(lèi)。
(4)一旦當(dāng)方法體執(zhí)行時(shí),出現(xiàn)異常,仍會(huì)在異常代碼處生成一個(gè)異常類(lèi)的對(duì)象,此對(duì)象滿足throws后的異常類(lèi)型時(shí),就會(huì)被拋出。異常代碼后續(xù)的代碼,就不再執(zhí)行!
(5)重寫(xiě)方法不能拋出比被重寫(xiě)方法范圍更大的異常類(lèi)型。在多態(tài)的情況下, 對(duì)methodA()方法的調(diào)用-異常的捕獲按父類(lèi)聲明的異常處理。
public class A {
public void methodA() throws IOException {
……
} }
public class B1 extends A {
public void methodA() throws FileNotFoundException {
……
} }
public class B2 extends A {
public void methodA() throws Exception { //報(bào)錯(cuò)
……
} }
4. try-catch-finally與throws的區(qū)別
(1)性質(zhì):
try-catch-finally:真正的將異常給處理掉了。throws的方式只是將異常拋給了方法的調(diào)用者,并沒(méi)有真正將異常處理掉。
(2)使用:
- 如果父類(lèi)中被重寫(xiě)的方法沒(méi)有
throws方式處理異常,則子類(lèi)重寫(xiě)的方法也不能使用throws,意味著如果子類(lèi)重寫(xiě)的方法中有異常,必須使用try-catch-finally方式處理。 - 執(zhí)行的方法a中,先后又調(diào)用了另外的幾個(gè)方法,這幾個(gè)方法是遞進(jìn)關(guān)系執(zhí)行的。我們建議這幾個(gè)方法使用
throws的方式進(jìn)行處理。而執(zhí)行的方法a可以考慮使用try-catch-finally方式進(jìn)行處理。
5. 手動(dòng)拋出異常throw
Java異常類(lèi)對(duì)象除在程序執(zhí)行過(guò)程中出現(xiàn)異常時(shí)由系統(tǒng)自動(dòng)生成并拋出,也可根據(jù)需要使用人工創(chuàng)建并拋出。
首先要生成異常類(lèi)對(duì)象,然后通過(guò)throw語(yǔ)句實(shí)現(xiàn)拋出操作(提交給Java運(yùn)行環(huán)境)。
IOException e = new IOException();
throw e;
可以拋出的異常必須是Throwable或其子類(lèi)的實(shí)例。下面的語(yǔ)句在編譯時(shí)將 會(huì)產(chǎn)生語(yǔ)法錯(cuò)誤: throw new String("want to throw");
6. 用戶自定義異常類(lèi)
(1)一般地,用戶自定義異常類(lèi)都是RuntimeException的子類(lèi)。
(2)自定義異常類(lèi)通常需要編寫(xiě)幾個(gè)重載的構(gòu)造器。
(3)自定義異常需要提供serialVersionUID 。
(3)自定義的異常通過(guò)throw拋出。
(4)自定義異常最重要的是異常類(lèi)的名字,當(dāng)異常出現(xiàn)時(shí),可以根據(jù)名字判斷異常類(lèi)型。
(5)用戶自定義異常類(lèi)MyException,用于描述數(shù)據(jù)取值范圍錯(cuò)誤信息。用戶自己的異常類(lèi)必須繼承現(xiàn)有的異常類(lèi)。
class MyException extends Exception {
static final long serialVersionUID = 13465653435L;
private int idnumber;
public MyException(String message, int id) {
super(message);
this.idnumber = id; }
public int getId() {
return idnumber;
}
}
public class MyExpTest {
public void regist(int num) throws MyException {
if (num < 0)
throw new MyException("人數(shù)為負(fù)值,不合理", 3);
else
System.out.println("登記人數(shù)" + num);
}
public void manager() {
try {
regist(100);
} catch (MyException e) {
System.out.print("登記失敗,出錯(cuò)種類(lèi)" +e.getId());
}
System.out.print("本次登記操作結(jié)束");
}
public static void main(String args[]) {
MyExpTest t = new MyExpTest();
t.manager();
}
}

到此這篇關(guān)于Java超詳細(xì)梳理異常處理機(jī)制的文章就介紹到這了,更多相關(guān)Java 異常處理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Redis?+?Java攔截器實(shí)現(xiàn)用戶匿名和非匿名訪問(wèn)
本文主要介紹了Redis?+?Java攔截器實(shí)現(xiàn)用戶匿名和非匿名訪問(wèn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06
java web SpringMVC后端傳json數(shù)據(jù)到前端頁(yè)面實(shí)例代碼
本篇文章主要介紹了java web SpringMVC后端傳json數(shù)據(jù)到前端頁(yè)面實(shí)例代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-03-03
java日期格式化SimpleDateFormat的使用詳解
這篇文章主要介紹了java SimpleDateFormat使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05
Java JDK與cglib動(dòng)態(tài)代理有什么區(qū)別
這篇文章主要介紹了Java JDK動(dòng)態(tài)代理和cglib動(dòng)態(tài)代理的區(qū)別文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2023-03-03
SpringBoot配置加載,各配置文件優(yōu)先級(jí)對(duì)比方式
這篇文章主要介紹了SpringBoot配置加載,各配置文件優(yōu)先級(jí)對(duì)比方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
Java數(shù)據(jù)結(jié)構(gòu)中的HashMap和HashSet詳解
HashMap和HashSet都是存儲(chǔ)在哈希桶之中,通過(guò)本文我們可以先了解一些哈希桶是什么,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧2023-10-10
在SpringBoot項(xiàng)目中的使用Swagger的方法示例
這篇文章主要介紹了在SpringBoot項(xiàng)目中的使用Swagger的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05

