## 一、概述
### 1.1 什么是異常?
* 異常本質上是程序上的錯誤。
* 錯誤在我們編寫程序的過程中會經常發生,包括編譯期間和運行期間的錯誤。
Java 中的異常(Exception)是一個在程序執行期間發生的事件,它中斷正在執行程序的正常指令流。為了能夠及時有效地處理程序中的運行錯誤,必須使用異常類。
### 1.2 異常分類
在 Java 中所有異常類型都是內置類 java.lang.Throwable 類的子類。Throwable 類下有兩個異常分支 Exception 和 Error。
* Exception 類用于用戶程序可能出現的異常情況,它也是用來創建自定義異常類型類的類。
* Error 定義了在通常環境下不希望被程序捕獲的異常。Error 類型的異常用于 Java 運行時由系統顯示與運行時系統本身有關的錯誤。它們通常是災難性的致命錯誤,不是程序可以控制的。
:-: 
其中異常類 Exception 又分為運行時異常和非運行時異常,這兩種異常有很大的區別,也稱為不檢查異常(Unchecked Exception)和檢查異常(Checked Exception)。
* `運行時異常`都是 RuntimeException 類及其子類異常,如 NullPointerException、IndexOutOfBoundsException 等,這些異常是不檢查異常,程序中可以選擇捕獲處理,也可以不處理。這些異常一般由程序邏輯錯誤引起,程序應該從邏輯角度盡可能避免這類異常的發生。
* `非運行時異常`是指 RuntimeException 以外的異常,類型上都屬于 Exception 類及其子類。從程序語法角度講是必須進行處理的異常,如果不處理,程序就不能編譯通過。如 IOException、ClassNotFoundException 等以及用戶自定義的 Exception 異常,一般情況下不自定義檢查異常。
【選擇】下列代碼中的異常屬于()(選擇兩項)
```
int a = 0;
System.out.println(2 / a);
```
```
A 非檢查型異常 B 檢查型異常 C Error D Exception
```
【選擇】()類及其子類所表示的異常是用戶程序無法處理的。(選擇一項)
```
A NumberFormatException B Exception C Error D RuntimeException
```
### 1.3 異常處理機制
Java 的異常處理機制提供了一種結構性和控制性的方式來處理程序執行期間發生的事件。異常處理通過 5 個關鍵字來實現:try、catch、throw、throws 和 finally。異常處理的機制如下:
* 在方法中用 try catch 語句捕獲并處理異常,catch 語句可以有多個,用來匹配多個異常。
* 對于處理不了的異常或者要轉型的異常,在方法的聲明處通過 throws 語句拋出異常,即由上層的調用方法來處理。
```
try {
邏輯程序塊
} catch (異常類型1 e) {
處理代碼塊1
} catch (異常類型2 e) {
處理代碼塊2
throw(e); // 再拋出這個“異常”
} finally {
釋放資源代碼塊
}
```
## 二、使用 try-catch-finally 實現異常處理
在 Java 中通常采用 try-catch-finally 語句來捕獲異常并處理。語法格式如下:
```
try {
邏輯代碼塊1
} catch (異常類型 e) {
處理代碼塊1
} finally {
釋放資源代碼
}
```
* 在以上語法中,把可能引發異常的語句封裝在 try 語句塊中,用以捕獲可能發生的異常。
* 如果 try 語句塊中發生異常,那么一個相應的異常對象就會被拋出,然后 catch 語句就會依據所拋出異常對象的類型進行捕獲,并處理。處理之后,程序會跳過 try 語句塊中剩余的語句,轉到 catch 語句塊后面的第一條語句開始執行。
* 如果 try 語句塊中沒有異常發生,那么 try 塊正常結束,后面的 catch 語句塊被跳過,程序將從 catch 語句塊后的第一條語句開始執行。
在以上語法的處理代碼塊1中,可以使用以下 3 個方法輸出相應的異常信息。
* `printStackTrace()`方法:指出異常的類型、性質、棧層次及出現在程序中的位置。
* `getMessage()`方法:輸出錯誤的性質。
* `toString()`方法:給出異常的類型與性質。
【例題】編寫一個錄入學生姓名、年齡和性別的程序,要求能捕捉年齡不為數字時的異常。
```
public class Student {
private String name;
private int age;
public Student() {}
public Student(String name, int age) {
this.setName(name);
this.setAge(age);
}
// getter setter...
// toString()
}
public class Test {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String name = "";
int age = 0;
try {
System.out.println("請輸入學生姓名:");
name = sc.next();
System.out.println("請輸入學生年齡:");
age = sc.nextInt();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(new Student(name, age));
}
}
```
在實際開發中,根據 try catch 語句的執行過程,try 語句塊和 catch 語句塊有可能不被完全執行,而有些處理代碼則要求必須執行。例如,程序在 try 塊里打開了一些物理資源(如數據庫連接、網絡連接和磁盤文件等),這些物理資源都必須顯式回收。
> Java 的垃圾回收機制不會回收任何物理資源,垃圾回收機制只回收堆內存中對象所占用的內存。
使用 try-catch-finally 語句時需注意以下幾點:
1. 異常處理語法結構中只有 try 塊是必需的,也就是說,如果沒有 try 塊,則不能有后面的 catch 塊和 finally 塊;
2. catch 塊和 finally 塊都是可選的,但 catch 塊和 finally 塊至少出現其中之一,也可以同時出現;
3. 可以有多個 catch 塊,捕獲父類異常的 catch 塊必須位于捕獲子類異常的后面;
4. 不能只有 try 塊,既沒有 catch 塊,也沒有 finally 塊;
5. 多個 catch 塊必須位于 try 塊之后,finally 塊必須位于所有的 catch 塊之后。
6. finally 與 try 語句塊匹配的語法格式,此種情況會導致異常丟失,所以不常見。
:-: 
但是當 try、catch、finally 中加入 return 之后,return 和 finally 的執行順序讓很多人混淆不清。
* `try`中帶有`return`
```
public static int show() {
try {
return 1;
} finally {
System.out.println("執行finally模塊");
}
}
// 執行 fianlly 模塊
// 1
```
* `try`和`catch`中都帶有`return`
```
public static int show() {
try {
int a = 8 / 0;
return 1;
} catch (Exception e) {
return 2;
} finally {
System.out.println("執行finally模塊");
}
}
// 執行 finally 模塊
// 2
```
> 當 try 代碼塊或者 catch 代碼塊中有 return 時,finally 中的代碼總會被執行,且 finally 語句 return 返回之前執行。
* `finally`中帶有`return`
```
public static int show() {
try {
int a = 8 / 0;
return 1;
} catch (Exception e) {
return 2;
} finally {
System.out.println("執行finally模塊");
return 0;
}
}
// 執行 finally 模塊
// 0
```
> 當 finally 有返回值時,會直接返回該值,不會去返回 try 代碼塊或者 catch 代碼塊中的返回值。
* `finally`中改變返回值
```
public static int show() {
int result = 0;
try {
return result;
} finally {
System.out.println("執行finally模塊");
result = 1;
}
}
// 執行finally模塊
// 0
```
> 由輸出結果可以看出,在 finally 代碼塊中改變返回值并不會改變最后返回的內容。
總結為以下幾條:
* 當 try 代碼塊和 catch 代碼塊中有 return 語句時,finally 仍然會被執行。
* 執行 try 代碼塊或?catch 代碼塊中的 return 語句之前,都會先執行 finally 語句。
* 無論在 finally 代碼塊中是否修改返回值,返回值都不會改變,仍然是執行 finally 代碼塊之前的值。
* finally 代碼塊中的?return 語句一定會執行。
【選擇】數組下標越界,則發生異常,提示為()(選擇一項)
```
A IOException
B ArithmeticException
C SQLException
D ArrayIndexOutOfBoundsException
```
【閱讀】運行下列代碼,當輸入的 num 值為 a 時,系統會輸出()
```
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
try {
int num = input.nextInt();
System.out.println("one");
} catch(Exception e) {
System.out.println("two");
} finally {
System.out.println("three");
}
System.out.println("end");
}
```
【閱讀】運行下列代碼,輸出結果為()
```
public static void main (String[] args) {
try {
int a = 1 - 1;
System.out.println("a = " + a);
int b = 4 / a;
int c[] = {1};
c[10] = 99;
} catch (ArithmeticException e) {
System.out.println("除數不允許為零");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("數組越界");
}
}
```
【選擇】下列關于異常的描述,錯誤的是()(選擇兩項)
```
A printStackTrace() 用來跟蹤異常事件發生時執行堆棧的內容
B catch 塊中可以出現同類型異常
C 一個 try 塊可以包含多個 catch 塊
D 捕獲到異常后將輸出所有 catch 語句塊的內容
```
【閱讀】假設要輸出的 id 值為 a101,name 值為 Tom,程序的執行結果為()
```
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
try {
int id = input.nextInt();
String name = input.next();
System.out.println("id=" + id);
System.out.println("name=" + name);
} catch (InputMismatchException e) {
System.out.println("輸入有誤");
System.exit(1);
e.printStackTrace();
} finally {
System.out.println("輸入結束");
}
}
```
【閱讀】下列代碼的運行結果為()
```
public static int test(int b) {
try {
b += 10;
return b;
} catch (Exception e) {
return 1;
} finally {
b += 20;
return b;
}
}
public static void main (String[] args) {
int num = 10;
System.out.println(test(num));
}
```
## 三、使用 throw 和 throws 實現異常處理
Java 中的異常處理除了包括捕獲異常和處理異常之外,還包括聲明異常和拋出異常,可以通過 throws 關鍵字在方法上聲明該方法要拋出的異常,然后在方法內部通過 throw 拋出異常對象。
* throws 用來聲明一個方法可能拋出的所有異常信息,throw 則是指拋出的一個具體的異常類型。
* 通常在一個方法(類)的聲明處通過 throws 聲明方法(類)可能拋出的異常信息,而在方法(類)內部通過 throw 聲明一個具體的異常信息。
* throws 通常不用顯示地捕獲異常,可由系統自動將所有捕獲的異常信息拋給上級方法; throw 則需要用戶自己捕獲相關的異常,而后再對其進行相關包裝,最后將包裝后的異常信息拋出。
### 3.1 throws 聲明異常
當一個方法產生一個它不處理的異常時,那么就需要在該方法的頭部聲明這個異常,以便將該異常傳遞到方法的外部進行處理。可以使用 throws 關鍵字在方法的頭部聲明一個異常,其具體格式如下:
```
返回值類型 方法名(參數列表) throws Exception 1, Exception2, … { … }
```
```
public static int test(int a, int b) throws ArithmeticException {
if (b == 0) {
throw new ArithmeticException("算術異常");
} else {
return a / b;
}
}
```
> 注意:在編寫類繼承代碼時要注意,子類在覆蓋父類帶 throws 子句的方法時,子類的方法聲明中的 throws 子句不能出現父類對應方法的 throws 子句中沒有的異常類型,因此 throws 子句可以限制子類的行為。也就是說,子類方法拋出的異常不會超過父類定義的范圍。
### 3.2 throw 拋出異常
throw 語句用來直接拋出一個異常,后接一個可拋出的異常類對象,其語法格式如下:
```
throw ExceptionObject;
```
> ExceptionObject 必須是 Throwable 類或其子類的對象。如果是自定義異常類,也必須是 Throwable 的直接或間接子類。
【選擇】下列關于這段代碼的說法正確的是()(選擇兩項)
```
public static void test (String str) throws Exception {
if (str == null || str.length == 0) {
throw new Exception("參數不能為空");
} else {
System.out.println("str = " + str);
}
}
public static void main(String[] args) throws Exception {
Scanner input = new Scanner(System.in);
String str = input.nextLine();
test(str);
}
```
```
A 代碼錯誤,沒有對 test() 方法中拋出的異常進行處理
B 若產生異常,將由系統進行異常處理
C 本段代碼對 throw 拋出異常對象的處理方法為自己拋出異常自己處理
D 若輸入空字符串,代碼運行結果為:java.lang.Exception: 參數不能為空
```
【選擇】在下列代碼劃線處不可以填入選項中的哪一個異常類型()(選擇一項)
```
public static int test(int a, int b) throws ____ {
if (b == 0) {
throw new ArithmeticException("算術異常");
} else {
return a / b;
}
}
```
```
A Throwable
B Exception
C InputMismatchException
D ArithmeticException
```
## 四、自定義異常
如果[](http://c.biancheng.net/java/) Java 提供的內置異常類型不能滿足程序設計的需求,這時我們可以自己設計 Java 類庫或框架,其中包括異常類型。實現自定義異常類需要繼承 Exception 類或其子類,如果自定義運行時異常類需繼承 RuntimeException 類或其子類。
自定義異常的語法形式為:
~~~
class 自定義異常名 extends Exception
~~~
在編碼規范上,一般將自定義異常類的類名命名為 XXXException,其中 XXX 用來代表該異常的作用。
自定義異常類一般包含兩個構造方法:一個是無參的默認構造方法,另一個構造方法以字符串的形式接收一個定制的異常消息,并將該消息傳遞給超類的構造方法。
【例題】編寫一個程序,對會員注冊時的年齡進行驗證,即檢測是否在 0~100 歲。
```
public class MyException extends Exception {
public MyException() {
super();
}
public MyException(String str) {
super(str);
}
}
import java.util.InputMismatchException;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
int age;
Scanner input = new Scanner(System.in);
System.out.println("請輸入您的年齡:");
try {
age = input.nextInt(); // 獲取年齡
if(age < 0) {
throw new MyException("您輸入的年齡為負數!輸入有誤!");
} else if(age > 100) {
throw new MyException("您輸入的年齡大于100!輸入有誤!");
} else {
System.out.println("您的年齡為:"+age);
}
} catch(InputMismatchException e1) {
System.out.println("輸入的年齡不是數字!");
} catch(MyException e2) {
System.out.println(e2.getMessage());
}
}
}
```
- 階段一 Java 零基礎入門
- 步驟1:基礎語法
- 第01課 初識
- 第02課 常量與變量
- 第03課 運算符
- 第04課 選擇結構
- 第05課 循環結構
- 第06課 一維數組
- 第08課 方法
- 第09課 數組移位與統計
- 第10課 基礎語法測試
- 第09課 基礎語法測試(含答案)
- 步驟2:面向對象
- 第01課 類和對象
- 第02課 封裝
- 第03課 學生信息管理
- 第04課 繼承
- 第05課 單例模式
- 第06課 多態
- 第07課 抽象類
- 第08課 接口
- 第09課 內部類
- 第10課 面向對象測試
- 第10課 面向對象測試(含答案)
- 步驟3:常用工具類
- 第01課 異常
- 第02課 包裝類
- 第03課 字符串
- 第04課 集合
- 第05課 集合排序
- 第06課 泛型
- 第07課 多線程
- 第08課 輸入輸出流
- 第09課 案例:播放器
- 第10課 常用工具測試(一)
- 第10課 常用工具測試(一)(答案)
- 第10課 常用工具測試(二)
- 第10課 常用工具測試(二)(答案)
- 階段二 從網頁搭建入門 JavaWeb
- 步驟1:HTML 與 CSS
- 第01課 HTML 入門
- 第01課 HTML 入門(作業)
- 第02課 CSS 入門
- 第02課 CSS 入門(作業)
- 第03課 CSS 布局
- 第03課 CSS 布局(作業)
- 步驟2:JavaScript 與前端案例
- 第01課 JavaScript 入門
- 第01課 JavaScript 入門(作業)
- 第02課 仿計算器
- 第03課 前端油畫商城案例
- 第04課 輪播圖
- 第05課 網頁搭建測試
- 第05課 網頁搭建測試(含答案)
- 步驟3:JavaScript 教程
- 入門
- 概述
- 基本語法
- 數據類型
- 概述
- 數值
- 字符串
- undefined, null 和布爾值
- 對象
- 函數
- 數組
- 運算符
- 算術運算符
- 比較運算符
- 布爾運算符
- 位運算符
- 運算順序
- 語法專題
- 數據類型的轉換
- 錯誤處理機制
- 標準庫
- String
- Date
- Math
- DOM
- 概述
- Document 節點
- 事件
- EventTarget 接口
- 事件模型
- 常見事件
- 階段三 數據庫開發與實戰
- 步驟1:初始數據庫操作
- 第01課 數據類型
- 第02課 表的管理
- 第03課 數據管理
- 第04課 常用函數
- 第05課 JDBC 入門
- 第06課 Java 反射
- 第07課 油畫商城
- 第08課 數據庫基礎測試
- 步驟2:MyBatis 從入門到進階
- 第01課 IntelliJ IDEA 開發工具入門
- 第02課 Maven 入門
- 第03課 工廠模式
- 第04課 MyBatis 入門
- 第05課 MyBatis 進階
- 第06課 商品信息管理
- 第07課 MyBatis 基礎測試
- 步驟3:Redis 數據庫與 Linux 下項目部署
- 第01課 Linux 基礎
- 第02課 Linux 下 JDK 環境搭建及項目部署
- 第03課 Redis 入門
- 階段四 SSM 到 Spring Boot 入門與綜合實戰
- 步驟1:Spring 從入門到進階
- 第01課 Spring 入門
- 第02課 Spring Bean 管理
- 第03課 Spring AOP
- 第04課 基于 AspectJ 的 AOP 開發
- 第05課 JDBC Template
- 第06課 Spring 事務管理
- 第07課 人員管理系統開發
- 第08課 Spring 從入門到進階測試
- 步驟2:Spring MVC 入門與 SSM 整合開發
- 第01課 Spring MVC 入門與數據綁定
- 第02課 Restful 風格的應用
- 第03課 SpringMVC 攔截器
- 第04課 辦公系統核心模塊
- 步驟3:Spring Boot 實戰
- 第01課 Spring Boot 入門
- 第02課 校園商鋪項目準備
- 第03課 校園商鋪店鋪管理
- 第04課 校園商鋪商品管理及前臺展示
- 第05課 校園商鋪框架大換血
- 步驟4:Java 面試
- 第01課 面試準備
- 第02課 基礎面試技巧
- 第03課 Web基礎與數據處理
- 第04課 主流框架