異常指的會是在程式碼執行期間,發生了不如預期 的錯誤,而不是單純的邏輯 上的錯誤,例如:
陣列的索引 進行操作時,誤將索引 寫成超出 陣列長度的情況,這就會出現ArrayIndexOfBoundsException(陣列索引超出範圍異常) 的狀況。邏輯錯誤而非異常。當發生異常而未進行處理時,JVM會因為錯誤而停止運作,Java中異常 的super class 是對應到java.lang.Throwable,裡面又可分為兩個:
Error:Java虛擬機無法處理的嚴重問題,一般不會寫針對性的程式碼進行處理這類型的問題(因為JVM會停止,只能針對程式碼進行修改)。例如:JVM系統內部錯誤、資源耗盡等的嚴重狀況。Exception:在寫程式碼時有錯誤,或者是使用者操作發生偶然的錯誤。例如:ArrayIndexOfBoundsException(陣列索引超出範圍異常) 、讀取不存在的文件等。Exception中又可分為兩種狀況:
編譯時 (受檢異常)出現異常。運行時 (非受檢異常)出現異常。常見運行時的異常:(以下程式碼省略最外層的部分,只寫核心的內容)
ArrayIndexOfBoundsException:int[] arr = new int[10];
System.out.println(arr[10]); //ArrayIndexOfBoundsException
NullPointerException``空指針異常:物件還沒有實例化僅有記憶體中的位址值,這時去查詢物件內的資料。int[] arr = new int[10];
int = null;
System.out.println(arr[0]); //NullPointerException
ClassCastException:當進行強制轉型時,型別不符合。String str = "hello";
Data date = (Date) str; //ClassCastException
NumberFormatException:數字轉換不符合類型。String str = "123";
int i = Integer.parseInt(str); // 可以
str = "123a";
int i1 = Integer.parseInt(str); //NumberFormatException
InputMismatchException:輸入的類型不符合對應類型。Scanner scan = new Scanner(System.in); //位在java.util底下的一個類,可以獲取使用者鍵盤輸入的內容
int i = scan.nextInt(); //如果使用者輸入數字可以將其賦值給變數
// 假設使用者輸入abc
System.out.println(i); //InputMismatchException
ArithmeticException:算數異常。int i = 10;
System.out.println(i / 0); //ArithmeticException
編譯時異常(必須在程式碼中另外處理)
可以使用try{} catch(Type err){} finally{} 的格式對可能出現異常的程式碼進行處理。
catch 比對異常類型)。catch,每個catch會有一個相對應的異常類型,當出現異常時,會依次進行異常類型 的比對,當比對到相同的異常類型時,則會忽略其他的catch。catch時,在catch 參數中的異常類型存在繼承 關係時,需要先從sub class開始寫。
不論是否有異常都會執行的程式碼。public class ExceptionHandleTest {
public static void main(String[] args) {
try {
int i = 10;
System.out.println(i / 0);
} catch(ArithmeticException e) {
System.out.println("The wrong is being handled");
}
System.out.println("Hello");
}
}
The wrong is being handled
Hello
當有使用catch將錯誤抓住的話,下面的Hello就可以正常被執行出來,但是像上方的這種異常因為是屬於程式碼本身就有瑕疵,所以需要去修改程式碼。
使用finally
public class ExceptionHandleTest {
public static void main(String[] args) {
try {
int i = 10;
System.out.println(i / 0);
} catch(ArithmeticException e) {
System.out.println("The wrong is being handled");
System.out.println(10 / 0);
} finally {
System.out.println("Finally");
}
System.out.println("Hello");
}
}
The wrong is being handled
Finally
假設catch中也存在其他的異常時,Hello就不會被執行了,但是在finally中的程式碼是不論怎麼樣都會執行(即使在try或catch中使用return )。
在實際開發中,有些資源的操作必須要手動的進行,否則GC不會主動的將它們回收,所以會將這些操作放在finally中。例如:資料庫連接、Socket等等。
使用throws異常的方式,如下method()僅是將異常向上丟給了使用它的方法method1(),但是在method1()中,還是需要去處理這個異常的問題。
public void method1() {
try {
method();
} catch(FileNotFoundException err) {
err.printStackTrace(); // 此方法會在控制台中印出完整的錯誤訊息
}
}
public void method() throws FileNotFoundException {
}
前面在重寫Override時有講過,sub class重寫方法時,若是有throws異常,異常的類型必須跟super class相同或是該異常類型的sub class,這是由於多態的關係。
在實作接口時,若是接口的抽象方法沒有throws則實作的方法也不能使用throws。
interface Flyable {
public void fly() throws Exception;
}
class Plane implements Flyable {
@Override
public void fly() throws Exception {
}
}
異常處理的選擇try-catch-finally or throws ?
try-catch-finally:
super class繼承的方法沒有寫throws。throws:
a()→b()→c()依序連貫進行時,在a()、b()、c()三個方法都會使用throws的方式,並且在實際使用這三個方法的位置進行try-catch-finally的處理。因為假設錯誤是發生在a(),當直接在a()中進行處理後,會繼續往b()執行,但是b()是依賴於a()的正常執行,這時又會接續b()和c()的錯誤,所以會統一在同時使用a()→b()→c()的方法中一次處理。throw:可以透過使用throw new (異常類型物件)的方式,手動將一個異常丟出如下:
public class ThrowTest {
public static void main(String[] args) {
Student student = new Student();
try {
student.register(-5);
System.out.println(student.id);
} catch(Exception err) {
err.printStackTrace(); // 輸入id非法
}
}
}
class Student {
int id;
public void register(int id) throws Exception {
if(id > 0) {
this.id = id;
} else {
throw new Exception("輸入id非法");
}
}
}
自定義異常類(可以參考jave.lang中異常類的寫法)。
RuntimeException或Exception兩種。overload的構造器。static final long serialVersionUID;。使用自定義異常類最主要的重點在於能夠透過這個異常類的名稱,就知道具體發生的異常狀況是甚麼。