寫在前面:
視訊是什麼東西,有看文檔精彩嗎?
視訊是什麼東西,有看文檔速度快嗎?
視訊是什麼東西,有看文檔效率高嗎?
1. 示範
諸小亮:異常,就是不正常,是在程式運作時出現的各種錯誤,比如:
public class Demo{
public static void main(String[] args){
int num = 2 / 0;//0不能作為除數,是以會産生異常
System.out.println("程式結束。。。。");
}
}
結果:
張小飛:之前也遇到過很多次了,可以具體解釋一下這輸出都是什麼意思嗎?
諸小亮:這是必須的,上圖中可以分為3個部分:
- ArithmeticException:是異常類型,
- / by zero:是異常資訊
- (Demo.java:5) 是異常出現的位置
張小飛:明白了
諸小亮:另外,出現異常後程式會終止,是以 System.out.println("程式結束。。。。"); 不運作
張小飛:噢,你不說還真沒注意,我記下了
2. 介紹
諸小亮:在 Java 中,用 Throwable類 對程式運作時的不正常情況進行了描述
張小飛:不對啊,上面的異常不是 ArithmeticException嗎?
諸小亮:你說的沒錯,但是 Java 中所有異常都是 Throwable 的子類,具體分為兩大類:
- Exception:可以通過修改代碼進行處理的不正常現象,比如:ArithmeticException
- Error:一般是系統底層問題,無法聽過代碼處理,比如:記憶體溢出
我們主要學習的是如何處理 Exception 這類異常
3. 處理異常
張小飛:那麼,如何處理異常呢?
諸小亮:可以使用 try catch 去處理
1. try catch
諸小亮:通過 try catch 代碼塊,捕獲指定的異常并處理,比如:
public static void main(String[] args){
try {
int num = 2 / 0;
}catch (Exception e){//catch 裡面捕獲指定的異常類型
//TODO 這裡書寫一些代碼處理這種異常情況
//e.getMessage():擷取異常資訊
System.out.println("發現錯誤了。。。。" + e.getMessage());
}
System.out.println("程式結束。。。。");
}
結果:
,程式可以正常結束
張小飛:這句代碼 ‘catch (Exception e)’,表示 Exception 的子類型,都可以捕獲到嗎?
諸小亮:是的,然後就會執行 catch 中的代碼
張小飛:您看我這樣寫行不行
諸小亮:當然沒問題,但是這樣的話,如果代碼中有其他異常,就不能catch到,比如:
public static void main(String[] args){
try {
int[] arr = new int[2];
System.out.println(arr[2]);
int num = 2 / 0;
}catch (ArithmeticException e){
System.out.println("發現算術異常。。。。" + e.getMessage());
}
System.out.println("程式結束。。。。");
}
結果:
解釋:因為我們 catch 的是 ArithmeticException ,卻出現了ArrayIndexOutOfBoundsException異常,是以并沒有走 catch中的代碼
張小飛:原來是這樣,我明白了
2. throws
諸小亮:下面我們看看——throws關鍵字
張小飛:這個是做什麼用的?
1. 示範
諸小亮;如果不想用 try catch 處理異常,可以通過 throws 關鍵字聲明異常,比如:
//throws 寫在方法上,用來聲明異常
public static int div(int a, int b) throws Exception{
return a / b;
}
張小飛:這是???
諸小亮:就是提醒調用者,這個方法可能發生異常
張小飛:您的意思是,在調用這個方法的時候需要用 try catch?
諸小亮:是的,誰調用誰處理,這時調用這個方法的地方就應該:
public static void main(String[] args){
try{
div(2,0);
}catch (Exception e){
//這裡處理異常
}
}
張小飛:如果調用者也不想處理呢?
諸小亮:那就繼續 throws,比如:
張小飛:這時候,如果異常,是誰處理?
諸小亮:就是 JVM 自己處理了
2. 多種異常
諸小亮:另外,throws 可以聲明多種異常,但是要逗号分隔,比如:
張小飛:這樣的話,catch 那裡怎麼辦呢?
諸小亮:如果聲明多個異常,catch 也應該有多個,保證每種異常都能被 cath 到并處理,比如:
public static void main(String[] args){
try{
div(2,0);
}catch (ArrayIndexOutOfBoundsException e){
}catch (ArithmeticException e){
}
}
//throws 寫在方法上,用來聲明異常
public static int div(int a, int b) throws ArrayIndexOutOfBoundsException, ArithmeticException{
return a / b;
}
張小飛:原來如此
諸小亮:還可以優化
張小飛:哦?怎麼優化?
諸小亮:因為上面的異常都是 Excepetion 的子類,是以直接 catch 一個 Excetion 就行了,比如:
張小飛:明白了
3. 常用方法
諸小亮:接着我們說說 Exception 中的常用方法
1. getMessage()
諸小亮:getMessage() ,用來擷取異常資訊,已經示範過,就不在多說了
張小飛:好的
2. toString
諸小亮:toString(),把異常轉換為字元串輸出,你可以嘗試一下
張小飛:我試過了,您看看
結果:
3. printStackTrace
諸小亮:printStackTrace(),列印異常的堆棧資訊,這個功能比較有用
張小飛:是這樣嗎?比如:
結果:
諸小亮:沒錯,上圖中,展示出了具體報錯的位置
4. throw關鍵字
諸小亮:接下來,我們看看——throw關鍵字
張小飛:咦,這個不是剛才說過來嗎?
諸小亮:看清楚,剛才說的是 throws,現在是 throw,不一樣
張小飛:哦哦,這個 throw 又是什麼意思?
諸小亮:throw,用來抛出指定的異常,比如:
public static void main(String[] args){
try {
int num = div(2,0);
} catch (Exception e) {
e.printStackTrace();
}
}
public static int div(int a, int b) throws Exception{
if(b == 0){
//通過 throw 關鍵字,抛出指定的異常,
//當程式運作到這裡後中斷,下面的 return a/b; 不再運作
throw new ArithmeticException("國小沒畢業嗎?除數不能是0---"+b);
}
return a / b;
}
結果:
張小飛:原來還可以這樣,不過這有什麼用呢?不用 throw 也會自動抛出異常啊?
諸小亮:這樣做,有兩個好處,第一,可以自定義異常資訊
張小飛:噢,确實,我們自定義的資訊更加清楚一些,第二呢?
諸小亮:第二,可以抛出其他異常,比如:
張小飛:明白了
諸小亮:另外,如果不知道應該抛出什麼異常,可以使用Exception,比如:
張小飛:我試了試,抛出這個是有問題的,您看看
諸小亮:因為,隻有 Throwable 類型的對象才能被 throw 抛出
張小飛:原來如此
諸小亮:來,我們總結一下 throw 跟 throws 的差別
- 位置不同
- throws:用在方法上,其後指定異常的種類可以有多個
- throw:用在方法内,其後指定具體的異常對象
- 功能不同
- throws:聲明異常,告訴調用者可能會出現問題
- throw:抛出異常,程式運作到此處時就中斷了
張小飛:明白了,不過,一般什麼時候用 throw 呢?
諸小亮:一般用來校驗參數,如果參數不對抛出異常
5. RuntimeException
諸小亮:下面我們說說——RuntimeException
張小飛:這個是什麼意思?
“RuntimeException,運作時異常,即:程式運作時抛出的異常,這是 Exception 下比較特殊的一個兒子”
“哦?怎麼個特殊法?”
“一般情況下,隻是在代碼中用 throw 抛出異常後,會編譯失敗,比如:”
“這時候需要在方法上使用 throws 聲明異常,比如:”
張小飛:不對吧,剛才我沒有用 throws ,也沒有編譯失敗啊
諸小亮:因為 IllegalArgumentException 是 RuntimeException 的子類
- 使用 throw 抛出運作時異常,不需要在方法上聲明
張小飛:原來如此
張小飛:那,ArithmeticException ArrayIndexOutOfBoundsException,也是運作時異常?
諸小亮:是的
張小飛:為什麼這樣呢?使用 throws 後,調用者就需要用 try catch 處理,這樣不是更好嗎?
如果不聲明的話,調用者就可能忘記使用 try catch,這樣程式容易報錯啊
諸小亮:你說的沒錯,不過出現RuntimeException,一般都是調用者寫的代碼有問題
這樣做的目的:就是停止程式,讓調用者修改代碼
張小飛:原來是這樣
諸小亮:因為 RuntimeException,是以可以把 Exception 分為兩類——受檢 和 非受檢異常
張小飛:這是什麼意思?
諸小亮:給你解釋一下
- 受檢異常:在編譯的時,要強制檢查的異常,這種異常需要去通過 try catch 來進行捕獲或者使用throws在方法上聲明,否則無法通過編譯的
- 非受檢異常:在編譯的時,不需要去強制檢查的異常,這種異常可以不需要顯示去捕獲或者抛出
張小飛:那麼,RuntimeException 就是非受檢異常?
諸小亮:沒錯,在 Exception 下,RuntimeException就是非受檢異常,其他的都是受檢異常
張小飛:明白了
6. 自定義異常
張小飛:自定義異常——是自己定義一個類,來描述某種異常嗎?
諸小亮:是的
張小飛:Java已經提供 Exception,為什麼還要自定義異常呢?
諸小亮:Java 雖然用 Exception 描述程式運作時的不正常情況,但是現實中的異常多種多樣
其内部的異常類型(Exception的子類)可能不滿足實際需求,這時候就要自定義異常
1. 示範
諸小亮:比如,定義一個方法做除法運算,如果除數小于等于0,就抛出一個除數是負數的異常
目前 Java 中沒有定義除數是負數的異常,這時候就需要自定義
示例:
//自定義異常,注意:一般使用自定義異常都是起一些容易了解的名字,這樣閱讀性更好
class FuShuException extends Exception{
}
public class Demo{
public static void main(String[] args) {
try {
//除數傳一個負數
int num = div(2,-1);
} catch (Exception e) {
e.printStackTrace();
}
}
public static int div(int a, int b) throws FuShuException {
if(b < 0){
throw new FuShuException();
}
if(b == 0){
throw new ArithmeticException("除數不能等于0。。。。。");
}
return a / b;
}
}
張小飛:沒有必要自定義吧,這樣子豈不是更好,您看看
諸小亮:你說的不錯,但是有時候需要針對不同的異常,做不同的處理,比如:
張小飛:明白了
2. 自定義異常資訊
張小飛:您定義的這個 FuShuException ,不能自定義異常資訊啊,IllegalArgumentException是可以的
他們不都是 Exception 的子類嗎?
諸小亮:這時因為,FuShuException少一個構造函數,比如:
FuShuException(String message){
super(message);//調用 super,設定異常資訊
}
張小飛:原來如此,我來試試
3. 繼承RuntimeException
諸小亮:另外,目前 FuShuException 繼承 Exception,必須在方法上用 throws 聲明。。。。
張小飛:這個簡單,直接繼承 RuntimeException 就行了,比如:
諸小亮:看來你已經充分了解:非受檢異常
7. finally
諸小亮:接着來看看——finally代碼塊
張小飛:這又是幹什麼用的?
1. 示範
諸小亮:finally 是配合 try catch 使用的一種特殊的代碼塊,比如:
public static void main(String[] args) {
try{
int num = 2 /0 ;
}catch (Exception e){
e.printStackTrace();
}finally {
System.out.println("finally中的代碼執行。。。。");
}
}
結果:
諸小亮:你有沒有什麼發現?
張小飛:finally 中的代碼,在 catch 處理異常後,正常執行
諸小亮:說得對,這就是它最重要的地方
2. finally中的代碼一定會執行
諸小亮:其實,finally代碼塊最重要的意義就是:它裡面的代碼一定會執行
張小飛:一定會執行?
“沒錯,即使在 catch 中使用 return 語句,finally也會執行,比如:”
public static void main(String[] args) {
try{
int num = 2 /0 ;
}catch (Exception e){
e.printStackTrace();
return;//catch中加上return語句,finally中的代碼依然執行
}finally {
System.out.println("finally中的代碼執行。。。。");
}
}
結果:
“有沒有什麼例外情況?”
“凡事都有例外,如果是退出 JVM 虛拟機,finally不執行,比如:”
結果:
3. return 和 finally
諸小亮:問你個問題,return後的語句和finally中的語句,哪個先執行?
張小飛:額....,稍等我驗證一下
public static void main(String[] args) {
show();
}
private static int show() {
try{
int num = 2 /0 ;
}catch (Exception e){
e.printStackTrace();
//return 後面調用一個方法
return test();
}finally {
System.out.println("finally中的代碼執行。。。。");
}
return 1;
}
private static int test() {
System.out.println("傳回一個0.....");
return 0;
}
結果:
張小飛:這跟我預想的結果不太一樣,本來以為 finally 中的代碼先執行呢?
諸小亮:它的執行過程是這樣的:
- 先執行 test 方法,拿到一個傳回值 0
- 再執行 finally 中的代碼
- 最後執行 return,把 0 給傳回
張小飛:明白了
8. try catch finally 的組合方式
諸小亮:下面給你介紹幾種 try catch finally 常用的組合方式
“第一種:try catch 單獨使用,這種最常見”
“第二種:一個try,多個catch”
“第三種:try 和 finally,沒有catch”
張小飛:“這樣也行?産生的異常時被誰處理了?”
諸小亮:“因為沒有catch,是以會自動把異常抛給調用者,但是 finally 中的代碼一定會執行”
9. 方法覆寫時的異常
諸小亮:在做繼承的時候,有幾點需要注意
第一:如果父類方法或父接口沒有 throws 異常,那麼子類複寫的方法也不能 throws 異常
class HeroException extends Exception{
}
class YaseException extends HeroException{
}
class DajiException extends Exception{
}
interface People{
void run();
}
class Person{
void eat(){
}
}
class child extends Person implements People{
//複寫父類的方法
@Override
void eat()throws HeroException {
System.out.println("吃飯。。。。。");
}
//實作父接口的方法
@Override
public void run()throws HeroException {
System.out.println("走。。。。。");
}
}
張小飛:上面的代碼,編譯失敗了
第二:子類方法聲明的異常類型,隻能是父類聲明的異常類型或者其子類型,比如:
張小飛:您的意思是,子類複寫後 run 方法後,聲明的異常隻能是 HeroException 或 其子類型?
諸小亮:是的,準确來說有 3 種方式可以選擇
第一種:聲明 HeroException
第二種:聲明 HeroException 的子類型
第三種:不聲明異常
張小飛:如果是聲明其他類型的異常呢?
諸小亮:那麼會直接編譯失敗,比如:
張小飛:如果父類方法沒聲明異常呢?
諸小亮:這是最特殊的一種情況,子類複寫方法後,可以聲明 RuntimeException 或 Error ,比如: