java開發兩年!這些異常處理的方式你得知道,不然你憑什麼漲薪!
前言
異常是在程式中導致程式中斷執行的一種指令流,當異常發生時,程式將直接中斷,不再執行後續的任何操作!
示例:兩數相除,若不處理任何異常,則只有在正確輸入兩個數字時,才能顯示出運算結果。
public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.println("請輸入第1個數字:"); String text1 = scanner.nextLine(); int x1 = Integer.parseInt(text1); System.out.println("請輸入第2個數字:"); String text2 = scanner.nextLine(); int x2 = Integer.parseInt(text2); System.out.println(x1/x2); }
當輸入的內容不為數字時,就會產生異常(NumberFormatException,數字格式異常):
當輸入的內容均為數字,但除數為0時,也會產生異常(ArithmeticException,算數運算異常):
作為程式設計師,不要想當然的認為使用者一定會嚴格按照你的要求去輸入內容,如果他們某一步輸入出現錯誤,則整個程式都會中斷,所以一定要對異常進行處理!以下是異常處理後的程式碼:
public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int x1; while (true) { System.out.println("請輸入第1個數字:"); String text1 = scanner.nextLine(); try { x1 = Integer.parseInt(text1); break; } catch (NumberFormatException e) { System.out.println("輸入有誤,請輸入數字!"); } } int x2; while (true) { System.out.println("請輸入第2個數字:"); String text2 = scanner.nextLine(); try { x2 = Integer.parseInt(text2); break; } catch (NumberFormatException e) { System.out.println("輸入有誤,請輸入數字!"); } } try { System.out.println(x1/x2); } catch (ArithmeticException e) { System.out.println("除數不能為0!"); } }
當輸入的內容不為數字時,提示輸入有誤,並提示重新輸入:
當除數為0時,提示除數不能為0:
這,就叫異常處理。
try-catch
如果要想對異常進行處理,則必須採用標準的處理格式,處理格式語法如下:
try {
// 有可能發生異常的程式碼段
} catch(異常型別1 物件名1) {
// 異常的處理操作
} catch(異常型別2 物件名2) {
// 異常的處理操作
} ...
finally {
// 異常的統一出口
}
處理流程:
1.一旦產生異常,則系統會自動產生一個異常類的例項化物件;
2.那麼,此時如果異常發生在try語句,則會自動找到匹配的catch語句執行,如果沒有在try語句中,則會將異常丟擲;
3.所有的catch根據方法的引數匹配異常類的例項化物件,如果匹配成功,則表示由此catch進行處理,
finally:
在進行異常的處理之後,在異常的處理格式中還有一個finally語句,那麼此語句將作為異常的統一出口,不管是否產生了異常,最終都要執行此段程式碼。即使沒有發生異常,在try中使用了return語句,finally仍然會執行。
異常的體系結構
異常指的是Exception,Exception類,在Java中存在一個父類Throwable(可能的丟擲)
Throwable存在兩個子類:
1.Error:表示的是錯誤,是JVM發出的錯誤操作,只能儘量避免,無法用程式碼處理。
2.Exception:一般表示所有程式中的錯誤,所以一般在程式中將進行try…catch的處理。
其中Exception包括以下兩種,它們的處理方式相同:
1.受檢異常:
當程式寫好後,編譯器會自動對所寫程式碼進行檢測,如果有問題,程式碼將會飄紅線。
例如:SQLException、IOException、ClassNotFoundException等。
2.非受檢異常:
即執行時異常(RunntimeException),編譯器無法對所寫程式碼異常進行檢測,程式將在會在執行時報錯。
例如:NullPointException、ArithmethicException、ClassCastException、ArrayIndexOutOfBundException等。
多異常捕獲的注意點:
1.捕獲更粗的異常不能放在捕獲更細的異常之前。
2.如果為了方便,則可以將所有的異常都使用Exception進行捕獲。
特殊的多異常捕獲寫法:
catch(異常型別1 | 異常型別2 物件名) {
//表示此塊用於處理異常型別1 和 異常型別2 的異常資訊
}
RuntimeExcepion與Exception的區別:
Integer類:public static int parseInt(String text)throws NumberFormatException
此方法丟擲了異常,但是使用時卻不需要進行try…catch捕獲處理,原因:
因為NumberFormatException並不是Exception的直接子類,而是RuntimeException的子類,只要是RuntimeException的子類,則表示程式在操作的時候可以不必使用try…catch進行處理(不飄紅線),如果有異常發生,則由JVM進行處理。當然,也可以通過try…catch處理。
throws關鍵字
在程式中異常的基本處理已經掌握了,但是隨異常一起的還有一個稱為throws關鍵字,此關鍵字主要在方法的宣告上使用,表示方法中不處理異常,而交給呼叫處處理。
格式:
返回值 方法名稱()throws Exception {
}
throw關鍵字
throw關鍵字表示在程式中人為的丟擲一個異常,因為從異常處理機制來看,所有的異常一旦產生之後,實際上丟擲的就是一個異常類的例項化物件,那麼此物件也可以由throw直接丟擲。
程式碼:
throw new Exception("");
自定義異常類(瞭解)
編寫一個類,繼承Exception,並重寫一參構造方法,即可完成自定義受檢異常型別。
編寫一個類,繼承RuntimeExcepion,並重寫一參構造方法,即可完成自定義執行時異常型別。
例如:
class MyException extends Exception { // 繼承Exception,表示一個自定義異常類
public MyException(String msg) {
super(msg); // 呼叫Exception中有一個引數的構造
}
}
自定義異常可以做很多事情, 例如:
class MyException extends Exception {
public MyException(String msg) {
super(msg);
//在這裡給維護人員發簡訊或郵件, 告知程式出現了BUG。
}
}
異常處理常見面試題
1.try-catch-finally中哪個部分可以省略?
答:catch和finally可以省略其中一個,catch和finally不能同時省略。
注意:格式上允許省略catch塊,但是發生異常時就不會捕獲異常了,在開發中也不會這樣去寫程式碼。
2.try-catch-finally中,如果catch中return了,finally還會執行嗎?
答:finally中的程式碼會執行。
執行流程:
1.先計算返回值,並將返回值儲存起來,等待返回;
2.執行finally程式碼塊;
3.將之前儲存的返回值返回出去。
需注意:
1.返回值是在finally運算之前就確定了,並且快取了,不管finally對該值做任何的改變,返回的值都不會改變。
2.finally程式碼中不建議包含return,因為程式會在上述的流程中提前退出,也就是說返回的值不是try或catch中的值。
3.如果在try或catch中停止了JVM,則finally不會執行。例如停電,或通過如下程式碼退出:
JVM:System.exit(0);
3.關於以下程式碼的輸出:
public class Student {
public static void main(String[] args) {
int studentId = test();
System.out.println("學生的學號是"+studentId);
}
public static int test() {
int studentId = 1000;
try {
return studentId;
} catch(Exception e) {
return 0;
} finally {
studentId=10;
}
}
}
正確答案是:學生的學號是1000
解:首先studentId是1000,然後執行到try,將要return學號,此時會對基本資料型別變數studentId的值1000進行備份,然後執行finally,studentId變為10,但是這隻改變了原有變數的值,備份的值不會發生變化,最終return的仍然是曾經的備份值,即1000。
4.和上題類似的案例:
public class Student {
public static void main(String[] args) {
StudentId s = test();
System.out.println("學生的學號是"+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;
}
}
正確答案是:學生的學號是10
解:首先棧記憶體中的s儲存了堆記憶體的地址,堆記憶體中s.studentId初始為1000,然後執行到try,將要return的是堆記憶體的地址,此時會對地址進行備份,然後執行finally,堆記憶體中s.studentId改為10,最終備份的地址被return出去,但實際上地址始終沒有發生過變化,s.studentId的值即為修改後的10。
最後
感謝你看到這裡,文章有什麼不足還請指正,覺得文章對你有幫助的話記得給我點個贊,每天都會分享java相關技術文章或行業資訊,歡迎大家關注和轉發文章!