Java自學第7期——異常(Exception)
1.概念:
- 異常 :指的是程式在執行過程中,出現的非正常的情況,終會導致JVM的非正常停止。
- 在Java等面向物件的程式語言中,異常本身是一個類,
產生異常就是建立異常物件並丟擲了一個異常物件。 - Java處理異常的方式是中斷處理。
異常指的並不是語法錯誤,語法錯了,編譯不通過,不會產生位元組碼檔案,根本不能執行.
2.Throwable體系:
- 所有異常或錯誤的超類:java.lang.Throwable ,其下有兩個子類:
java.lang.Error 與 java.lang.Exception
平常所說的異常指 java.lang.Exception 。 - 異常與錯誤的區別
Error:無法通過處理解決的叫錯誤,只能事先避免。
Exception:異常產生後程序員可以通過程式碼的方式糾正,使程式繼續執行,是必須要處理的。 - Throwable中的常用方法:
public void printStackTrace() :列印異常的詳細資訊。
包含了異常的型別,異常的原因,還包括異常出現的位置,
在開發和除錯階段,都得使用printStackTrace。
public String getMessage() :獲取發生異常的原因。 提示給使用者的時候,就提示錯誤原因。
public String toString() :獲取異常的型別和異常描述資訊(不用)。 - 出現異常,把異常的類名複製到API中查詢
3.異常的分類
編譯時期異常:checked異常。在編譯時期,就會檢查,如果沒有處理異常,則編譯失敗。(如日期格式化異常)
執行時期異常:runtime異常。在執行時期,檢查異常.在編譯時期,執行異常不會編譯器檢測(不報錯)。(如數學異常)
public class Demo01 { public static void main(String[] args) { int[] array = {1,2,3}; /* System.out.println(array[3]); 異常如下: Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3 at Exception.Demo01.main(Demo01.java:30) 其中,java.lang.ArrayIndexOutOfBoundsException表示異常的型別 Index 3 out of bounds for length 3表示異常的原因 at Exception.Demo01.main(Demo01.java:30)表示異常的位置 */ } }
4.異常的處理
在java中,提供了一個throw關鍵字,它用來丟擲一個指定的異常物件。那麼,丟擲一個異常具體如何操作呢?
- 建立一個異常物件。封裝一些提示資訊(資訊可以自己編寫)。
- 需要將這個異常物件告知給呼叫者,通過關鍵字throw完成。throw異常物件。
throw用在方法內,用來丟擲一個異常物件,將這個異常物件傳遞到呼叫者處,並結束當前方法的執行。
- 丟擲異常:
throw new 異常類名(引數) - 判斷引數合法性,看引數是否為空,統一使用Object類裡的靜態方法requireNonNull();
public class Demo02 {
public static void main(String[] args) {
int[] array = {1,2,3};
/*
此時index=3越界了,看看執行結果
Exception in thread "main" java.lang.IndexOutOfBoundsException: 小老弟,你給的角標越陣列的界了
at Exception.Demo02.getElement(Demo02.java:30)
at Exception.Demo02.main(Demo02.java:23)
*/
int num = getElement(array,3);
System.out.println(num);
}
public static int getElement(int[] arr,int index){
if(index < 0||index > arr.length-1){
//此時角標越界了,使用throw丟擲異常
throw new ArrayIndexOutOfBoundsException("小老弟,你給的角標越陣列的界了");
}
//該方法返回所給角標對應陣列元素
int Element = arr[index];
return Element;
}
public static void method(Object obj){
//對傳遞的obj進行引數的合法性判斷
//傳給Object的方法requireNonNull
Objects.requireNonNull(obj);
}
}
-
宣告異常關鍵字throws
異常處理的第一種方式:交給別人處理
宣告異常:將問題標識出來,報告給呼叫者。如果方法內通過throw丟擲了編譯時異常,而沒有捕獲處理(稍後講 解該方式),
那麼必須通過throws進行宣告,讓呼叫者去處理。
關鍵字throws運用於方法宣告之上,用於表示當前方法不處理異常,而是提醒該方法的呼叫者來處理異常(丟擲異常).
宣告異常格式:
修飾符 返回值型別 方法名(引數) throws 異常類名1,異常類名2…{ } -
throws用於進行異常類的宣告,若該方法可能有多種異常情況產生,那麼在throws後面可以寫多個異常類,用逗號隔開。
public class Demo03 {
public static void main(String[] args) throws IOException{//此處注意
read("a.txt");
}
public static void read(String path) throws FileNotFoundException, IOException {
if (!path.equals("a.txt")){
throw new FileNotFoundException("檔案不存在");
}
if (!path.equals("b.txt")){
throw new IOException("檔案不存在");
}
}
}
-
捕獲異常:try...catch
如果異常出現的話,會立刻終止程式,所以我們得處理異常:- 該方法不處理,而是宣告丟擲,由該方法的呼叫者來處理(throws)。
- 在方法中使用try-catch的語句塊來處理異常。
try-catch的方式就是捕獲異常。
-
捕獲異常:Java中對異常有針對性的語句進行捕獲,可以對出現的異常進行指定方式的處理。
捕獲異常語法如下:
try(
可能存在異常的程式碼
)catch(定義一個變數,用來接受try中異常的物件){
處理異常的程式碼
}
...
catch(異常類名 變數名){
}
注意:try和catch都不能單獨使用,必須連用。try中可能丟擲多個異常物件,那麼可以使用多個catch處理 -
如何獲取異常的資訊:Throwable類中定義了三個檢視方法:
public String getMessage() :返回此throwable的簡短描述。
public String toString() :返回此throwable的詳細描述。
public void printStackTrace() :列印異常物件,預設此方法,列印的異常資訊是最全面的。
包含了異常的型別,異常的原因,還包括異常出現的位置,在開發和除錯階段,都得使用printStackTrace(); -
finally程式碼塊:有一些特定的程式碼無論異常是否發生,都需要執行。另外,因為異常會引發程式跳轉,導致有些語句執行 不到。而finally就是解決這個問題的,在finally程式碼塊中存放的程式碼都是一定會被執行的。
什麼時候的程式碼必須終執行?
當我們在try語句塊中打開了一些物理資源(磁碟檔案/網路連線/資料庫連線等),我們都得在使用完之後,終關閉開啟 的資源。 -
finally的語法:
try...catch....finally:自身需要處理異常,最終還得關閉資源。
注意:
finally不能單獨使用。
只有try或者catch中呼叫了退出JVM的方法時finally才不會執行,否則finally永遠會執行
public class Demo04_try_catch {
public static void main(String[] args) {
try {
readProblem("a.tx");
} catch (IOException obj) {//try中什麼型別異常,括號內就寫什麼型別
System.out.println("1:"+obj.getMessage());
System.out.println("2:"+obj.toString());
//printStackTrace最全面,預設列印異常物件也是使用該方法
obj.printStackTrace();
System.out.println("3:"+obj);
}finally{//有一些特定的程式碼無論異常是否發生 論程式碼是否異常,fanally程式碼塊都會執行
System.out.println("fianlly程式碼塊執行!");
}
}
public static void readProblem(String path1) throws IOException {
if (!path1.equals("b.txt")){
throw new IOException("檔案字尾名不對");
}
}
}
5.異常注意事項
(一).捕獲多個異常如何處理
- 多個異常分別處理。
- 多個異常一次捕獲,多次處理。
- 多個異常一次捕獲一次處理。一般使用一次捕獲多次處理方式
注意:
a.多個catch內異常類不能相同,且若有子父類關係,子類需要寫在上面
b.如果父類丟擲了多個異常,子類重寫父類方法時,丟擲和父類相同的異常或者是父類異常的子類或者不丟擲異常。
c.父類方法沒有丟擲異常,子類重寫父類該方法時也不可丟擲異常。此時子類產生該異常,只能捕獲處理,不能宣告丟擲.
d.執行時異常(runtime)被丟擲可以不處理。即不捕獲也不宣告丟擲。
e.如果finally有return語句,永遠返回finally中的結果,即避免在finally中寫return語句.
public class Demo05_Tips {
public static void main(String[] args){
try{
int[] arr1 = {1,2,3};
System.out.println(arr1[3]);
//try裡多個異常一次捕獲
List<Integer> list = List.of(1,2,3);
System.out.println(list.get(3));
} catch(ArrayIndexOutOfBoundsException a){//通過catch括號內參數型別接受對應異常物件
System.out.println(a);
} catch (IndexOutOfBoundsException a){
System.out.println(a);
}
}
public void show0() throws FileNotFoundException{};
public void show1() throws IndexOutOfBoundsException{};
public void show2() throws ArrayIndexOutOfBoundsException{};
public void show3() throws IOException{};
}
class zi extends Demo05_Tips{
//子類重寫父類方法時,可以丟擲和父類相同的異常
public void show0() throws FileNotFoundException{};
//丟擲父類異常類的子類
public void show1() throws ArrayIndexOutOfBoundsException{};
//可以不丟擲異常
public void show2() {};
}
定義好異常類後,進行測試:
如果使用者註冊重複,返回異常資訊
6.自定義異常類:
java提供的異常類不夠,需要進行自定義異常類:
public class ***Exception extends Exception/RuntimeException{
//新增一個空引數的構造方法
//新增一個帶異常資訊的構造方法
}
注意:
1.自定義異常類一般以Exception結尾,表示這是異常類
2.自定義異常類必須繼承Exception類或者RuntimeException類,
繼承Exception類就是編譯期異常,選擇throws或者try catch
繼承RuntimeException就是執行期異常,交給虛擬機器處理.
//首先定義一個登陸異常類RegisterException:
//此處繼承異常類,本身類名是就是自定義異常類名
public class RegisterException extends Exception{
//模擬註冊操作,如果使用者名稱已存在,則丟擲異常並提示:親,該使用者名稱已經被註冊。
//原有使用者名稱
//新增一個空引數的構造方法
public RegisterException() {
super();
}
//新增一個帶異常資訊的構造方法,讓父類來處理
public RegisterException(String s){
super(s);
}
}
使用:
public class Demo07_test {
static String[] username0 = {"1","2","3"};
public static void main(String[] args) throws RegisterException {//宣告自定義異常類
System.out.println("請輸入使用者名稱");
Scanner sc = new Scanner(System.in);
String str = sc.next();
Check(str);
}
public static void Check(String str) throws RegisterException {//宣告自定義異常類
for (String name : username0){
if (name.equals(str)){
throw new RegisterException("該使用者名稱已經被註冊");
}
}
System.out.println("註冊成功。");
}
}