- 第12章 異常Exception
- 快速入門
- 異常介紹
- 異常體系圖一覽!
- 異常體系圖
- 異常體系圖的小結
- 常見的執行時異常
- 常見的執行時異常包括
- 常見的執行時異常舉例
- 編譯異常
- 常見的編譯異常
- 異常處理
- 基本介紹
- 異常處理的方式
- 示意圖
- try-catch 異常處理
- try-catch 方式處理異常說明
- try-catch 方式處理異常細節
- 練習
- try-catch-finally 執行順序小結
- 課後練習題
- throws 異常處理
- 基本介紹
- 快速入門案例
- 注意事項和使用細節
- 自定義異常
- 基本概念
- 自定義異常的步驟
- 自定義異常的應用例項
- throw 和 throws 的區別
- 練習
第12章 異常Exception
快速入門
將可能出現異常的程式碼塊選中->快捷鍵 ctrl + alt + t
-> 選中 try-catch
package com.hspedu.exception_;
public class Exception01 {
public static void main(String[] args) {
int num1 = 10;
int num2 = 0;//Scanner();
//2. 當執行到 num1 / num2 因為 num2 = 0, 程式就會出現(丟擲)異常 ArithmeticException
//3. 當丟擲異常後,程式就退出,崩潰了, 下面的程式碼就不再執行
//4. 不應該出現了一個不算致命的問題就導致整個系統崩潰
//5. java 設計者,提供了一個叫異常處理機制來解決該問題
//如果程式設計師,認為一段程式碼可能出現異常/問題,可以使用try-catch異常處理機制來解決,從而保證程式的健壯性
//將該程式碼塊->選中->快捷鍵 ctrl + alt + t -> 選中 try-catch
//6. 如果進行異常處理,那麼即使出現了異常,程式可以繼續執行
try {
int res = num1 / num2;
} catch (Exception e) {
//e.printStackTrace();
System.out.println("出現異常的原因=" + e.getMessage());//輸出異常資訊
}
System.out.println("程式繼續執行....");
}
}
異常介紹
Java語言中,將程式執行中發生的不正常情況稱為“異常”。(開發過程中的語法錯誤和邏輯錯誤不是異常)
執行過程中所發生的異常事件可分為兩大類:
1.Error(錯誤):Java虛擬機器無法解決的嚴重問題。如:JVM系統內部錯誤、資源耗盡等嚴重情況。比如: StackOverflowError[棧溢位] 和 OOM(out of memory).
Error是嚴重錯誤,程式會崩潰。
2.Exception:其它因程式設計錯誤或偶然的外在因素導致的一般性問題,可以使用針對性的程式碼進行處理。例如空指標訪問,試圖讀取不存在的檔案,網路連線中斷等等,Exception分為兩大類:
- 執行時異常[程式執行時,發生的異常]
- 編譯時異常[程式設計時,編譯器檢查出的異常]
異常體系圖一覽!
異常體系圖
異常體系圖的小結
- 異常分為兩大類,執行時異常和編譯時異常.
- 執行時異常,編譯器檢查不出來。一般是指程式設計時的邏輯錯誤,是程式設計師應該避免其出現的異常。
java.lang.RuntimeException
類及它的子類都是執行時異常。 - 對於執行時異常,可以不作處理,因為這類異常很普遍,若全處理可能會對程式的可讀性和執行效率產生影響。
- 編譯時異常,是編譯器要求必須處置的異常。
常見的執行時異常
常見的執行時異常包括
NullPointerException
空指標異常ArithmeticException
數學運算異常ArrayIndexOutOfBoundsException
陣列下標越界異常ClassCastException
型別轉換異常NumberFormatException
數字格式不正確異常[]
常見的執行時異常舉例
NullPointerException
空指標異常
當應用程式試圖在需要物件的地方使用null 時,丟擲該異常。
public class NullPointerException_ {
public static void main(String[] args) {
String name = null; // 空指標出現異常
System.out.println(name.length());
}
}
-
ArithmeticException
數學運算異常當出現異常的運算條件時,丟擲此異常。例如,一個整數“除以零”時,丟擲此類的一個例項。
ArrayIndexOutOfBoundsException
陣列下標越界異常
用非法索引訪問陣列時丟擲的異常。如果索引為負或大於等於陣列大小,則該索引為非法索引。
ClassCastException
型別轉換異常
當試圖將物件強制轉換為不是例項的子類時,丟擲該異常。例如,以下程式碼將生成一個ClassCastException
。
public class ClassCastException_ {
public static void main(String[] args) {
A b = new B(); //向上轉型
B b2 = (B)b;//向下轉型,這裡是OK
C c2 = (C)b;//這裡丟擲ClassCastException
}
}
class A {}
class B extends A {}
class C extends A {}
NumberFormatException
數字格式不正確異常
當應用程式試圖將字串轉換成一種數值型別,但該字串不能轉換為適當格式時,丟擲該異常=> 使用異常我們,可以確保輸入是滿足條件數字.
public class NumberFormatException_ {
public static void main(String[] args) {
String name = "timerring";
//將String 轉成 int
int num = Integer.parseInt(name); // 丟擲NumberFormatException
System.out.println(num);
}
}
編譯異常
編譯異常是指在編譯期間,就必須處理的異常,否則程式碼不能透過編譯。
常見的編譯異常
SQLException
:運算元據庫時,查詢表可能發生異常
IOException
:操作檔案時,發生的異常
FileNotFoundException
:當操作一個不存在的檔案時,發生異
ClassNotFoundException
:載入類,而該類不存在時,異常
EOFException
:操作檔案,到檔案末尾,發生異常
lllegalArguementException
:引數異常
異常處理
基本介紹
異常處理就是當異常發生時,對異常處理的方式。
異常處理的方式
-
try-catch-finally:程式設計師在程式碼中捕獲發生的異常,自行處理
-
throws:將發生的異常丟擲,交給呼叫者(方法)來處理,最頂級的處理者就是JVM。
示意圖
如果程式設計師沒有顯示地處理異常,則預設是throws。
try-catch 異常處理
try-catch 方式處理異常說明
-
Java提供try和catch塊來處理異常。try塊用於包含可能出錯的程式碼。catch塊用於處理try塊中發生的異常。可以根據需要在程式中有多個try...catch塊。
-
基本語法
try {
//可疑程式碼
//將異常生成對應的異常物件,傳遞給catch塊
}catch(異常){
//對異常的處理
}
//如果沒有finally,語法是可以透過
try-catch 方式處理異常細節
-
如果異常發生了,則異常發生後面的程式碼不會執行,直接進入到catch塊。
-
如果異常沒有發生,則順序執行try的程式碼塊,不會進入到catch。
-
如果希望不管是否發生異常,都執行某段程式碼(比如關閉連線,釋放資源等)
則使用如下程式碼 finally {} -
可以有多個catch語句,捕獲不同的異常(進行不同的業務處理),要求父類異
常在後,子類異常在前,比如(Exception
在後,NullPointerException
在前),如果發生異常,只會匹配一個catch。(因為如果在前面都讓Exception
捕獲了,後面寫子類捕獲就沒有用了)。package com.hspedu.try_; public class TryCatchDetail02 { public static void main(String[] args) { //1.如果try程式碼塊有可能有多個異常 //2.可以使用多個catch 分別捕獲不同的異常,相應處理 //3.要求子類異常寫在前面,父類異常寫在後面 try { Person person = new Person(); //person = null; System.out.println(person.getName());//NullPointerException int n1 = 10; int n2 = 0; int res = n1 / n2;//ArithmeticException } catch (NullPointerException e) { System.out.println("空指標異常=" + e.getMessage()); } catch (ArithmeticException e) { System.out.println("算術異常=" + e.getMessage()); } catch (Exception e) { System.out.println(e.getMessage()); } finally { } } } class Person { private String name = "jack"; public String getName() { return name; } }
-
可以進行
try-finally
配合使用,這種用法相當於沒有捕獲異常,因此程式會
直接崩掉/退出。應用場景就是執行一段程式碼,不管是否發生異常,都必須執行某個業務邏輯。public class TryCatchDetail03 { public static void main(String[] args) { /* 可以進行 try-finally 配合使用, 這種用法相當於沒有捕獲異常, 因此程式會直接崩掉/退出。應用場景,就是執行一段程式碼,不管是否發生異 常,都必須執行某個業務邏輯 */ try{ int n1 = 10; int n2 = 0; System.out.println(n1 / n2); }finally { System.out.println("執行了finally.."); // 執行完直接退出 } System.out.println("程式繼續執行.."); // 不會執行 } }
練習
package com.hspedu.try_;
public class TryCatchExercise01 {
}
class Exception01 {
public static int method() {
try {
String[] names = new String[3];//String[]陣列
if (names[1].equals("tom")) {//NullPointerException
System.out.println(names[1]);
} else {
names[3] = "hspedu";
}
return 1;
} catch (ArrayIndexOutOfBoundsException e) {
return 2;
} catch (NullPointerException e) {//捕獲
return 3; // 但是不從這裡返回
} finally { // 必須執行,相當於沒有異常捕獲
return 4; // 返回4
}
}
public static void main(String[] args) {
System.out.println(method()); // 輸出4
}
}
package com.hspedu.try_;
public class TryCatchExercise03 {
}
class ExceptionExe01 {
public static int method() {
int i = 1;//i = 1
try {
i++;// i=2
String[] names = new String[3];
if (names[1].equals("tom")) { //空指標
System.out.println(names[1]);
} else {
names[3] = "hspedu";
}
return 1;
} catch (ArrayIndexOutOfBoundsException e) {
return 2;
} catch (NullPointerException e) {
return ++i; // i = 3 => 儲存臨時變數 temp = 3;
} finally {
++i; //i = 4
System.out.println("i=" + i);// i = 4
}
}
public static void main(String[] args) {
System.out.println(method());// 3
}
}
// 最終輸出 i = 4 3
try-catch-finally 執行順序小結
- 如果沒有出現異常,則執行try塊中所有語句,不執行catch塊中語句,如果有finally,最後還需要執行finally裡面的語句。
- 如果出現異常,則try塊中異常發生後,try塊剩下的語句不再執行。將執行catch塊中的語句,如果有finally,最後還需要執行finally裡面的語句。
課後練習題
如果使用者輸入的不是一個整數,就提示他反覆輸入,直到輸入一個整數為止
package com.hspedu.try_;
import java.util.Scanner;
public class TryCatchExercise04 {
public static void main(String[] args) {
//如果使用者輸入的不是一個整數,就提示他反覆輸入,直到輸入一個整數為止
//思路
//1. 建立Scanner物件
//2. 使用無限迴圈,去接收一個輸入
//3. 然後將該輸入的值,轉成一個int
//4. 如果在轉換時,丟擲異常,說明輸入的內容不是一個可以轉成int的內容
//5. 如果沒有丟擲異常,則break 該迴圈
Scanner scanner = new Scanner(System.in);
int num = 0;
String inputStr = "";
while (true) {
System.out.println("請輸入一個整數:"); //
inputStr = scanner.next();
try {
num = Integer.parseInt(inputStr); //這裡是可能丟擲異常
break;
} catch (NumberFormatException e) {
System.out.println("你輸入的不是一個整數:");
}
}
System.out.println("你輸入的值是=" + num);
}
}
throws 異常處理
基本介紹
- 如果一個方法(中的語句執行時)可能生成某種異常,但是並不能確定如何處理這種異常,則此方法應顯示地宣告丟擲異常,表明該方法將不對這些異常進行處理,而由該方法的呼叫者負責處理。
- 在方法宣告中用throws語句可以宣告丟擲異常的列表,throws後面的異常型別可以是方法中產生的異常型別,也可以是它的父類。
快速入門案例
throws後面的異常型別可以是方法中產生的異常型別(也可以是異常列表,丟擲多個異常),也可以是它的父類(例如 Exception)。
package com.hspedu.throws_;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class Throws01 {
public static void main(String[] args) {
}
public void f2() throws FileNotFoundException,NullPointerException,ArithmeticException {
//建立了一個檔案流物件
//1. 這裡的異常是一個FileNotFoundException 編譯異常
//2. 使用前面講過的 try-catch-finally
//3. 使用throws ,丟擲異常, 讓呼叫f2方法的呼叫者(方法)處理
//4. throws後面的異常型別可以是方法中產生的異常型別,也可以是它的父類(例如 Exception)
//5. throws 關鍵字後也可以是 異常列表, 即可以丟擲多個異常
FileInputStream fis = new FileInputStream("d://aa.txt");
}
}
注意事項和使用細節
-
對於編譯異常,程式中必須處理,比如try-catch或者throws。
-
對於執行時異常,程式中如果沒有處理,預設就是throws的方式處理(相當於方法後有一個throws XXXException,這時逐級向上,最後main方法上可能也預設throws,這時就給JVM處理)。
-
子類重寫父類的方法時,對丟擲異常的規定:子類重寫的方法,所丟擲的異常型別要麼和父類丟擲的異常一致,要麼為父類丟擲的異常的型別的子型別。
-
在throws過程中,如果有方法 try-catch,就相當於處理異常,就可以不必 throws。
例如子類有一個編譯異常,使用throws丟擲,那麼父類也必須對該異常做出反應,或是throws,或者try catch,否則同樣是編譯異常。
自定義異常
基本概念
當程式中出現了某些“錯誤”,但該錯誤資訊並沒有在 Throwable
子類中描述處理,這個時候可以自己設計異常類,用於描述該錯誤資訊。
自定義異常的步驟
- 定義類:自定義異常類名(程式設計師自己寫)繼承Exception或RuntimeException
-
如果繼承Exception,屬於編譯異常
-
如果繼承RuntimeException,屬於執行異常(一般來說,繼承RuntimeException)
自定義異常的應用例項
當我們接收Person物件年齡時,要求範圍在18-120之間,否則丟擲一個自定義異常(要求繼承RuntimeException),並給出提示資訊。
package com.hspedu.customexception_;
public class CustomException {
// 方法宣告處,throws 異常
public static void main(String[] args) /*throws AgeException*/ {
int age = 180;
//要求範圍在 18 – 120 之間,否則丟擲一個自定義異常
if(!(age >= 18 && age <= 120)) {
//這裡我們可以透過構造器,設定資訊
// 在方法體中,這裡 throw 物件
throw new AgeException("年齡需要在 18~120之間");
}
System.out.println("你的年齡範圍正確.");
}
}
// 自定義一個異常
// 1. 一般情況下,我們自定義異常是繼承 RuntimeException
// 2. 即把自定義異常做成 執行時異常,好處是我們可以使用預設的處理機制,即自動向上throws異常,否則main中也得加throws。
class AgeException extends RuntimeException {
public AgeException(String message) {//構造器
super(message); // 呼叫父構造器,可以進入原始碼逐級檢視。
}
}
父構造器:
public Throwable(String message) {
fillInStackTrace();
detailMessage = message; // 傳入 detailmessage
}
throw 和 throws 的區別
練習
程式設計題
編寫應用程式,接收命令列的兩個引數(整數),計算兩數相除。
計算兩個數相除,要求使用方法 cal(int n1, int n2)
對資料格式不正確(NumberFormatException)、缺少命令列引數(ArrayIndexOutOfBoundsException)、除0 進行異常處理(ArithmeticException)。
package com.hspedu.homework;
public class Homework01 {
public static void main(String[] args) {
/*
編寫應用程式EcmDef.java,接收命令列的兩個引數(整數),計算兩數相除。
計算兩個數相除,要求使用方法 cal(int n1, int n2)
對資料格式不正確(NumberFormatException)、缺少命令列引數(ArrayIndexOutOfBoundsException)、除0 進行異常處理(ArithmeticException)。
*/
try {
//先驗證輸入的引數的個數是否正確 兩個引數
if(args.length != 2) {
throw new ArrayIndexOutOfBoundsException("引數個數不對");
}
//先把接收到的引數,轉成整數
int n1 = Integer.parseInt(args[0]);
int n2 = Integer.parseInt(args[1]);
double res = cal(n1, n2);//該方法可能丟擲ArithmeticException
System.out.println("計算結果是=" + res);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println(e.getMessage());
} catch (NumberFormatException e) {
System.out.println("引數格式不正確,需要輸出整數");
} catch (ArithmeticException e) {
System.out.println("出現了除0的異常");
}
}
//編寫cal方法,就是兩個數的商
public static double cal(int n1, int n2) {
return n1 / n2;
}
}