前言
異常是在程序中導(dǎo)致程序中斷運(yùn)行的一種指令流此疹,當(dāng)異常發(fā)生時(shí)甚颂,程序?qū)⒅苯又袛啵辉賵?zhí)行后續(xù)的任何操作秀菱!
示例:兩數(shù)相除振诬,若不處理任何異常,則只有在正確輸入兩個(gè)數(shù)字時(shí)衍菱,才能顯示出運(yùn)算結(jié)果赶么。
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("請(qǐng)輸入第1個(gè)數(shù)字:");
String text1 = scanner.nextLine();
int x1 = Integer.parseInt(text1);
System.out.println("請(qǐng)輸入第2個(gè)數(shù)字:");
String text2 = scanner.nextLine();
int x2 = Integer.parseInt(text2);
System.out.println(x1/x2);
}
當(dāng)輸入的內(nèi)容不為數(shù)字時(shí),就會(huì)產(chǎn)生異常(NumberFormatException脊串,數(shù)字格式異常):
當(dāng)輸入的內(nèi)容均為數(shù)字辫呻,但除數(shù)為0時(shí)清钥,也會(huì)產(chǎn)生異常(ArithmeticException,算數(shù)運(yùn)算異常):
作為程序員放闺,不要想當(dāng)然的認(rèn)為用戶一定會(huì)嚴(yán)格按照你的要求去輸入內(nèi)容祟昭,如果他們某一步輸入出現(xiàn)錯(cuò)誤,則整個(gè)程序都會(huì)中斷怖侦,所以一定要對(duì)異常進(jìn)行處理篡悟!以下是異常處理后的代碼:
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int x1;
while (true) {
System.out.println("請(qǐng)輸入第1個(gè)數(shù)字:");
String text1 = scanner.nextLine();
try {
x1 = Integer.parseInt(text1);
break;
} catch (NumberFormatException e) {
System.out.println("輸入有誤,請(qǐng)輸入數(shù)字匾寝!");
}
}
int x2;
while (true) {
System.out.println("請(qǐng)輸入第2個(gè)數(shù)字:");
String text2 = scanner.nextLine();
try {
x2 = Integer.parseInt(text2);
break;
} catch (NumberFormatException e) {
System.out.println("輸入有誤搬葬,請(qǐng)輸入數(shù)字!");
}
}
try {
System.out.println(x1/x2);
} catch (ArithmeticException e) {
System.out.println("除數(shù)不能為0艳悔!");
}
}
當(dāng)輸入的內(nèi)容不為數(shù)字時(shí)急凰,提示輸入有誤,并提示重新輸入:
當(dāng)除數(shù)為0時(shí)猜年,提示除數(shù)不能為0:
這抡锈,就叫異常處理。
try-catch
如果要想對(duì)異常進(jìn)行處理乔外,則必須采用標(biāo)準(zhǔn)的處理格式企孩,處理格式語(yǔ)法如下:
try {
// 有可能發(fā)生異常的代碼段
} catch(異常類型1 對(duì)象名1) {
// 異常的處理操作
} catch(異常類型2 對(duì)象名2) {
// 異常的處理操作
} ...
finally {
// 異常的統(tǒng)一出口
}
處理流程:
1.一旦產(chǎn)生異常,則系統(tǒng)會(huì)自動(dòng)產(chǎn)生一個(gè)異常類的實(shí)例化對(duì)象袁稽;
2.那么勿璃,此時(shí)如果異常發(fā)生在try語(yǔ)句,則會(huì)自動(dòng)找到匹配的catch語(yǔ)句執(zhí)行推汽,如果沒有在try語(yǔ)句中补疑,則會(huì)將異常拋出;
3.所有的catch根據(jù)方法的參數(shù)匹配異常類的實(shí)例化對(duì)象歹撒,如果匹配成功莲组,則表示由此catch進(jìn)行處理,
finally:
在進(jìn)行異常的處理之后暖夭,在異常的處理格式中還有一個(gè)finally語(yǔ)句锹杈,那么此語(yǔ)句將作為異常的統(tǒng)一出口,不管是否產(chǎn)生了異常迈着,最終都要執(zhí)行此段代碼竭望。即使沒有發(fā)生異常,在try中使用了return語(yǔ)句裕菠,finally仍然會(huì)執(zhí)行咬清。
異常的體系結(jié)構(gòu)
異常指的是Exception,Exception類,在Java中存在一個(gè)父類Throwable(可能的拋出)
Throwable存在兩個(gè)子類:
1.Error:表示的是錯(cuò)誤旧烧,是JVM發(fā)出的錯(cuò)誤操作影钉,只能盡量避免,無(wú)法用代碼處理掘剪。
2.Exception:一般表示所有程序中的錯(cuò)誤平委,所以一般在程序中將進(jìn)行try…catch的處理。
其中Exception包括以下兩種夺谁,它們的處理方式相同:
1.受檢異常:
當(dāng)程序?qū)懞煤罅猓幾g器會(huì)自動(dòng)對(duì)所寫代碼進(jìn)行檢測(cè),如果有問題予权,代碼將會(huì)飄紅線。
例如:SQLException浪册、IOException扫腺、ClassNotFoundException等。
2.非受檢異常:
即運(yùn)行時(shí)異常(RunntimeException)村象,編譯器無(wú)法對(duì)所寫代碼異常進(jìn)行檢測(cè)笆环,程序?qū)⒃跁?huì)在運(yùn)行時(shí)報(bào)錯(cuò)。
例如:NullPointException厚者、ArithmethicException躁劣、ClassCastException、ArrayIndexOutOfBundException等库菲。
多異常捕獲的注意點(diǎn):
1.捕獲更粗的異常不能放在捕獲更細(xì)的異常之前账忘。
2.如果為了方便,則可以將所有的異常都使用Exception進(jìn)行捕獲熙宇。
特殊的多異常捕獲寫法:
catch(異常類型1 | 異常類型2 對(duì)象名) {
//表示此塊用于處理異常類型1 和 異常類型2 的異常信息
}
RuntimeExcepion與Exception的區(qū)別:
Integer類:public static int parseInt(String text)throws NumberFormatException
此方法拋出了異常鳖擒,但是使用時(shí)卻不需要進(jìn)行try…catch捕獲處理,原因:
因?yàn)镹umberFormatException并不是Exception的直接子類烫止,而是RuntimeException的子類蒋荚,只要是RuntimeException的子類,則表示程序在操作的時(shí)候可以不必使用try…catch進(jìn)行處理(不飄紅線)馆蠕,如果有異常發(fā)生期升,則由JVM進(jìn)行處理。當(dāng)然互躬,也可以通過try…catch處理播赁。
throws關(guān)鍵字
在程序中異常的基本處理已經(jīng)掌握了,但是隨異常一起的還有一個(gè)稱為throws關(guān)鍵字吼渡,此關(guān)鍵字主要在方法的聲明上使用行拢,表示方法中不處理異常,而交給調(diào)用處處理。
格式:
返回值 方法名稱()throws Exception {
}
throw關(guān)鍵字
throw關(guān)鍵字表示在程序中人為的拋出一個(gè)異常舟奠,因?yàn)閺漠惓L幚頇C(jī)制來看竭缝,所有的異常一旦產(chǎn)生之后,實(shí)際上拋出的就是一個(gè)異常類的實(shí)例化對(duì)象沼瘫,那么此對(duì)象也可以由throw直接拋出抬纸。
代碼:
throw new Exception("");
自定義異常類(了解)
編寫一個(gè)類,繼承Exception耿戚,并重寫一參構(gòu)造方法湿故,即可完成自定義受檢異常類型。
編寫一個(gè)類膜蛔,繼承RuntimeExcepion秩霍,并重寫一參構(gòu)造方法溯泣,即可完成自定義運(yùn)行時(shí)異常類型。
例如:
class MyException extends Exception { // 繼承Exception,表示一個(gè)自定義異常類
public MyException(String msg) {
super(msg); // 調(diào)用Exception中有一個(gè)參數(shù)的構(gòu)造
}
}
自定義異车猓可以做很多事情搭综, 例如:
class MyException extends Exception {
public MyException(String msg) {
super(msg);
//在這里給維護(hù)人員發(fā)短信或郵件疮方, 告知程序出現(xiàn)了BUG充边。
}
}
異常處理常見面試題
1.try-catch-finally中哪個(gè)部分可以省略?
答:catch和finally可以省略其中一個(gè)蘑辑,catch和finally不能同時(shí)省略洋机。
注意:格式上允許省略catch塊,但是發(fā)生異常時(shí)就不會(huì)捕獲異常了洋魂,在開發(fā)中也不會(huì)這樣去寫代碼绷旗。
2.try-catch-finally中,如果catch中return了副砍,finally還會(huì)執(zhí)行嗎刁标?
答:finally中的代碼會(huì)執(zhí)行。
執(zhí)行流程:
1.先計(jì)算返回值址晕,并將返回值存儲(chǔ)起來膀懈,等待返回;
2.執(zhí)行finally代碼塊谨垃;
3.將之前存儲(chǔ)的返回值返回出去启搂。
需注意:
1.返回值是在finally運(yùn)算之前就確定了,并且緩存了刘陶,不管finally對(duì)該值做任何的改變胳赌,返回的值都不會(huì)改變。
2.finally代碼中不建議包含return匙隔,因?yàn)槌绦驎?huì)在上述的流程中提前退出疑苫,也就是說返回的值不是try或catch中的值。
3.如果在try或catch中停止了JVM,則finally不會(huì)執(zhí)行捍掺。例如停電撼短,或通過如下代碼退出:
JVM:System.exit(0);
3.關(guān)于以下代碼的輸出:
public class Student {
public static void main(String[] args) {
int studentId = test();
System.out.println("學(xué)生的學(xué)號(hào)是"+studentId);
}
public static int test() {
int studentId = 1000;
try {
return studentId;
} catch(Exception e) {
return 0;
} finally {
studentId=10;
}
}
}
正確答案是:學(xué)生的學(xué)號(hào)是1000
解:首先studentId是1000,然后執(zhí)行到try挺勿,將要return學(xué)號(hào)曲横,此時(shí)會(huì)對(duì)基本數(shù)據(jù)類型變量studentId的值1000進(jìn)行備份,然后執(zhí)行finally不瓶,studentId變?yōu)?0禾嫉,但是這只改變了原有變量的值,備份的值不會(huì)發(fā)生變化蚊丐,最終return的仍然是曾經(jīng)的備份值熙参,即1000。
4.和上題類似的案例:
public class Student {
public static void main(String[] args) {
StudentId s = test();
System.out.println("學(xué)生的學(xué)號(hào)是"+s.studentId);
}
public static StudentId test() {
StudentId s = new StudentId();
s.studentId = 1000;
try {
return s;
} catch(Exception e) {
return s;
} finally {
s.studentId=10;
}
}
public static class StudentId {
int studentId;
}
}
正確答案是:學(xué)生的學(xué)號(hào)是10
解:首先棧內(nèi)存中的s存儲(chǔ)了堆內(nèi)存的地址麦备,堆內(nèi)存中s.studentId初始為1000孽椰,然后執(zhí)行到try,將要return的是堆內(nèi)存的地址泥兰,此時(shí)會(huì)對(duì)地址進(jìn)行備份弄屡,然后執(zhí)行finally题禀,堆內(nèi)存中s.studentId改為10鞋诗,最終備份的地址被return出去,但實(shí)際上地址始終沒有發(fā)生過變化迈嘹,s.studentId的值即為修改后的10削彬。
最后
感謝你看到這里,文章有什么不足還請(qǐng)指正秀仲,覺得文章對(duì)你有幫助的話記得給我點(diǎn)個(gè)贊融痛,每天都會(huì)分享java相關(guān)技術(shù)文章或行業(yè)資訊,歡迎大家關(guān)注和轉(zhuǎn)發(fā)文章神僵!