異常--異常體系、異常的處理、異常的捕獲、finally語句塊和自定義異常
異常
異常的概念
異常,就是不正常的意思。在生活中,意思說你的身體某個部位出現異常了,該部位和正常情況不一樣,該部位的功能將會受到影響,在程式中的意思是:
- 異常:知道是在程式的執行過程中,出現的非正常情況,最終會導致JVM的非正常停止。
在Java等面向物件的程式語言中,異常本身就是一個類,產生異常就是建立異常物件並丟擲了一個異常物件。Java處理異常的方式是中斷處理。
備註:異常指的並不是語法錯誤,語法錯誤編譯無法通過,就不會產生位元組碼檔案,根本不會執行。
異常體系
異常機制其實是幫助我們找到程式中出現的問題,異常的根類是 java.lang.Throwble ,它的下面有兩個子類: java.lang.Error 和 java.lang.Exception ,平常說的異常知道是 java.lang.Exception 。
-
Error:嚴重錯誤Error,無法通過,好比絕症,只能是事先預防。
-
Exception:異常。異常產生後程序員可以通過程式碼的方式進行糾正處理,是程式能夠繼續進行下去,好比感冒,闌尾炎。
異常產生的過程解析:
public static void main(String[] args) { // 建立int型別的陣列 int[] arr = {1,2,3}; int e = getElement(arr, 3); System.out.println(e); } /* * 引數: * int[] arr * int index */ public static int getElement(int[] arr,int index) { int e = arr[index]; return e; }
異常的處理
Java異常處理的無關關鍵字:try catch finally throw throws
丟擲異常throw
當我們在編寫程式的時候,我們必須要考慮程式可能出現問題的情況。比如說:在定義方法的是,方法需要接受引數,那麼對於呼叫者藍色,當呼叫方法的時候需要接收引數,首先需要對引數資料進行合法的判斷,若不合法,就應該概述呼叫者,傳遞合法的資料進來。這時候就需要使用丟擲異常的方式來告訴呼叫者。
在Java當中,提供了一個關鍵字throw,它用來丟擲一個指定的異常物件,步驟:
-
建立一個異常物件,封裝一些提示資訊(資訊可以自己編寫)。
-
需要將這個異常物件告知給呼叫者,通過關鍵字throw就可以完成。throw丟擲一個異常物件
throw-般我們用在方法內,用來丟擲一個具體的異常物件,將這個異常物件丟擲給呼叫者處,並結束當前方法的執行。
使用格式:
throw new 異常類名(引數)
示例:
//給呼叫者丟擲一個空指標異常
throw new NillPointerException("要訪問的arr陣列不存在,值為unull")
//給呼叫者丟擲一個索引越界異常
throw new IndexOutOfBounds Except ion( "該索引超出了引用的範圍");
Objects非空判斷
在該類中,提供了一些靜態的實用方法,這時方法是null-save(空指標安全)或者null-tolerant(容忍空指標),那麼在它的原始碼中,對 物件的null值進行了丟擲異常的操作。
- public static
T requireNonNul(T obj):檢視指定的引用物件不是null值
public static <T> T requireNonNul1(T obj) {
if (obj == nul1)
throw new NullPointerException();
return obj;
}
宣告異常處理
宣告異常:將問題標識出來,報告給呼叫者,如果方法內通過throw丟擲了一個編譯異常,又沒有通過捕獲處理(try..catch),那麼必須通過throw進行宣告,讓呼叫者取處理。
關鍵字throws運用在方法的宣告上,用於表示不處理異常T提醒呼叫者該方法的呼叫攜帶的有異常資訊,誰呼叫誰處理。
宣告異常處理的格式
修飾符 返回值型別 方法名(引數列表) throw 異常類名1,異常類名2...{}
示例程式碼:
public class DemoThros{
public static void main(String[] args){
read("C:/a.txt");
}
public static void read(String path){
//校驗如果你傳遞的路徑不是以.txt的結尾的,丟擲呼叫名檔案找不到異常
if (path. endsWith(" . txt")){
//丟擲一個檔案找不到異常
throw new FileNotFoundException(“檔案找不到");
}
}
}
捕獲異常try...catch
如果異常出現的話,會立刻終止程式。
-
如果使用throw關鍵字來宣告式處理,由該方法呼叫者來處理
-
在方法中使用try...catch的語句來處理異常
try...catch的方式就是捕獲異常
- 捕獲異常:java中對異常有針對性的語句塊來進行捕獲,可以對出現的異常進行指定的方式處理。
捕獲異常的格式:
try{
//編寫的可能會出現的異常程式碼
//.....
} catch (異常型別e) {
//處理異常的邏輯程式碼
//記錄日誌//列印異常資訊//繼續往上拋
}
try:該語句塊中可能出現異常的程式碼
catch:用來進行某種異常的捕獲,實現對捕獲到的異常進行處理。
備註: try和catch都不能單獨使用,一般建議連用。
程式碼演示:
public class TryCatchDemo {
public static void main(String[] args) {
try{
readFile("D:\\a.txt");
}catch(FileNotFoundException e){//括號中需要定義什麼異常型別?
//try中丟擲的是什麼型別,在括號中就定義成什麼樣的異常型別
//列印異常資訊
System.out.println(e);
}
System.out.println("程式往下載入了。。。");
}
public static void readFile(String path) {
if (!path. startsWith("C:")){
throw new FileNotFoundException(“傳遞的檔案不是在C盤中,檔案找不到異常");
}
}
}
如何獲取異常的資訊
Throwable類中定義了一些常用的api方法
-
public String getMessage():獲取異常的描述資訊,原因(提示給使用者看的,提示錯誤資訊)
-
public String toString():獲取異常的型別、異常的描述資訊
-
public void printStackTrace():列印異常的跟蹤棧資訊並輸出到控制檯中。
包含了異常的型別,異常的原因,還包括異常出現的位置,在開發和除錯階段般都是使用printStackTrace()方法
finally語句塊
finally:有一些它的的程式碼無論是否發生,都需要執行。當程式傳送異常時描繪引發程式的跳躍性,導致有一些程式碼載入不到,而finally語句塊就是用來解決這樣的問題的,在finally語句塊中存放的程式碼一般都是一定會被執行到的
什麼樣的程式碼最終一定會把執行到的?
比如說,在try中打開了一些物理資源(磁碟檔案/網路連線/資料庫的連線),我們一般在使用完畢後必須關閉掉,可以使用finally語句塊來實現。
finally語句塊語法格式:
trr{
//
}catch(異常型別 e){
//...
}
....
finally{
//....
}
備註:中間的catch語句塊可以省略,finally不能單獨使用,建議連用。
程式碼演示:
public class FinallyDemo06{
public static void main(String[] args){
try {
system.out. printIn("開啟IO流");
readFile("b.txt");
} catch (IOException e) {
e .printStackTrace();
}finally{
System out .println("不管程式如何執行,此處程式碼塊定會被載入到");
System. out . println("關閉IO流");
}
System. out . println("over");
}
public static void readFile(String path) throw IOException{
if ( lpath. endsWith(" .txt")) {
throw new IOException("檔案的字尾名不對。 。。")
}
System. out . println("檔案的字尾名正確,讀取到了該檔案");
}
}
備註:
-
如果finally語句塊有return語句,永遠返回的是finally語句塊中的內容。
-
當只有在try或者catch中都有退出JVM的相關方法,此時finally才不會被執行到,否則finally永遠會執行。
異常的注意事項
當程式中出現了多個異常那麼該如何捕獲有該如何處理?
- 多個異常分別處理
- 多個異常異常捕獲,多次處理
- 多個異常一次捕獲,一次處理
一般我們都是使用一次捕獲,多次處理方式,格式如下:
try{
//可能出現一次的程式碼 多個異常
}catch(異常型別A e){//當try中出現A異常的時候,就該catch來捕獲
//處理異常的邏輯
}catch(異常型別B b){//當try中出現B異常的時候,就該catch來捕獲
//處理異常的邏輯
}
....
....
注意:這種異常處理方式,要求多個catch的異常不能相同,並且若catch中的多個異常之間存在父類關係,那麼子類異常的處理在父類異常的上面,父類異常的處理在下面。
-
執行時異常被丟擲可以不處理,即不捕獲也不宣告丟擲
-
如果finally中有return語句,那麼永遠返回finally語句塊中的結果值
-
如果父類丟擲了多個異常,子類重寫父類方法時,丟擲和父類相同的異常或者說是父類異常的子類或者不丟擲異常
-
父類方法如果沒有丟擲異常,子類重寫該方法也不可能丟擲異常。此時子類方法內產生了異常,只能捕獲處理,不能宣告丟擲。
自定義異常
概述:
Java中根據不同的情況提供了不同的異常類,JDK官方提供的異常類始終是有限的,大多數我們需要根據自己的業務需要自定義異常。例如:年齡負數異常。成績負數異常,登入異常,註冊異常等等。
什麼是自定義異常類?
在開發中根據自己的業務的異常情況來自定義的異常類
例如:我想自定義一個業務邏輯異常類:註冊異常 RegisterException
異常類如何定義?
-
自定義一個編譯期異常,自定義類繼承於 java.lang.Exception 。
-
自定義一個執行期異常類,自定義繼承於 java.lang.RuntimeException 。
自定義異常的練習:
模擬註冊操作,如果使用者存在,則丟擲異常並提示:親,你的名字已經被註冊了,再換個名字吧!
// 首先 定義一個註冊異常類 RegisterException --->業務邏輯異常類
public class RegisterException extends RuntimeException {
// 空參構造
public RegisterException(){}
// 有參構造
public RegisterException(String message) {
super(message);
}
}
public class Demo {
// 使用陣列模擬資料庫 已存在多個賬戶名稱
public static String[] names = {"小孫","小王","小趙"};
public static void main(String[] args) {
// Scanner
Scanner scan = new Scanner(System.in);
String name = scan.next();
// 校驗賬戶是否已被註冊過
try {
// 可能會引發異常的程式碼
checkName(name);// 如果沒有發生異常,就代表註冊成功
System.out.println("註冊成功!")
names = Arrays.copyOf(names,names.length+1);
names[names.length-1] = name;
} catch (RegisterException e) {
// 異常的處理邏輯
e.printStackTrace();
}
}
// 校驗賬戶是否已被註冊
public static boolean checkName(String usernmae) throws RegisterException {
for (String name : names) {
if (name.equals(username)) {
// 表明 名字已經註冊過了,就丟擲註冊異常
throw new RegisterException("親,您的名字已經被註冊了,再換個名字吧!");
}
}
return true;
}
}