第1關:Java中的異常處理機制
任務描述
本關任務:完成異常類選擇題。
為了完成本關任務,你需要掌握:
1.什么是異常;
2.如何使用異常。
什么是異常
異常:程序在運行過程中產生的不正常情況。
程序在運行的時候,發生了一些不被預期的事件,從而沒有按照我們編寫的代碼執行,這就是異常。
異常是Java中的錯誤,但是並不是所有的錯誤都是異常,比如說,你在定義變量名的時候沒有依照Java的規則,在語句的結尾少了一個分號,那么運行出來結果是提示是錯誤 java.lang.Error;
不過如果你用System.out.println(5/0),那這個時候你的程序是可以正常編譯的,但是在運行的時候,因為你用0做了除數,會拋出 java.lang.ArithmeticException 的異常。
異常的原因有很多種,比如:
1.輸入了錯誤的數據,比如:程序需要的是int類型數據,而用戶輸入了一串字符串;
對象沒有初始化就調用:下面這段代碼就會提示空指針異常;
String str = null;
int length = str.length();
- 要打開的文件不存在;
- 網絡通信時連接中斷,或者JVM內存溢出。
要理解Java中的異常是如何工作的,你需要掌握以下三種類型的異常:
- 檢查性異常;
- 運行時異常;
- 錯誤。
檢查性異常
例如我們要打開一個文件時,這段代碼就可能存在異常,因為這個文件很有可能並不存在。
File f = new File("G://test.java");
FileInputStream fs = new FileInputStream(f);
這里G盤下的test.java文件就可能不存在,這個時候運行這段代碼就可能會出異常,所以在我們編寫代碼的時候IDE會提示我們要處理這段代碼可能出現的異常。
如果我們不處理的話,程序是不能被編譯的(即必須要處理)。
運行時異常
運行時異常程序員可以不去處理(即可以處理,也可以不處理),當異常出現時,虛擬機會處理。常見的運行時異常有空指針異常。
常見的運行時異常:
ClassCastException(類轉換異常)
IndexOutOfBoundsException(數組越界)
NullPointerException(空指針)
ArrayStoreException(數據存儲異常,操作數組時類型不一致)
錯誤
錯誤不是異常,而是脫離程序員控制的問題(即處理不了)。錯誤在代碼中通常被忽略。例如,當棧溢出時,一個錯誤就發生了,它們在編譯也檢查不到的。
Exception 類
所有的異常類都是從 java.lang.Exception類繼承的子類。
Exception 類是 Throwable類的子類。除了Exception類之外,Throwable還有一個子類Error 。
Java 程序通常不捕獲錯誤。錯誤一般發生在嚴重故障時,它們在Java程序處理的范疇之外。
Error 用來指示運行時環境發生的錯誤。
例如,JVM內存溢出。一般地,程序不會從錯誤中恢復。
異常類有兩個主要的子類:IOException 類和 RuntimeException 類。

第2關:捕獲異常
任務描述
本關任務:捕獲程序的異常,輸出異常處理的結果。
相關知識
為了完成本關任務,你需要掌握:1.如何捕獲異常。
捕獲異常
通過第一關我們知道,有一部分異常是需要程序員提前處理的,這種異常統一稱為檢測性異常,如果我們不處理,程序是不能編譯通過的,在IDE中也會出現一條紅線。

這個時候我們就必須處理這段可能出現異常的程序。
如何處理呢?
Java中提供了一個捕獲異常的機制:try-catch

通過這兩個單詞的字面意思我們就能很好的理解了:try:嘗試,catch:捕獲;
嘗試執行代碼A和代碼B如果這兩段代碼有一個出現了異常,就會執行catch中的語句,如果代碼A、B都不存在異常就不會執行catch代碼,最后繼續執行代碼C。
所以之前報錯的代碼我們這樣寫就沒錯啦:

在這里我們可以發現catch捕獲的是FileNotFoundException,這是一個文件未找到異常,所以我們在捕獲異常的時候最好要先明確異常的種類是什么。
好奇的同學可能會有疑惑,檢測性異常可以用try-catch來處理,那運行時異常可不可以用try-catch來處理呢?答:運行時異常也可以用try-catch來處理
可不可以呢?自己驗證一下吧!
編程要求
請仔細閱讀右側代碼,根據方法內的提示,在Begin - End區域內進行代碼補充,具體任務如下:
編輯器中的代碼運行時可能會有異常,請利用本關知識處理該異常。
測試說明
補充完代碼后,點擊測評,平台會對你編寫的代碼進行測試,當你的結果與預期輸出一致時,即為通過。
輸入:
4
2
輸出:
2
輸入:
4
0
輸出:
除數不能為0
提示:捕獲異常需要用特定的類,下表總結了常用的異常類:
非檢測型異常:
| 異常 | 描述 |
|---|---|
| ArithmeticException | 當出現異常的運算條件時,拋出此異常。例如,一個整數”除以零”時,拋出此類的一個實例。 |
| ArrayIndexOutOfBoundsException | 用非法索引訪問數組時拋出的異常。如果索引為負或大於等於數組大小,則該索引為非法索引。 |
| ClassCastException | 當試圖將對象強制轉換為不是實例的子類時,拋出該異常。 |
| IllegalArgumentException | 拋出的異常表明向方法傳遞了一個不合法或不正確的參數。 |
| IllegalMonitorStateException | 拋出的異常表明某一線程已經試圖等待對象的監視器,或者試圖通知其他正在等待對象的監視器而本身沒有指定監視器的線程。 |
| IllegalStateException | 在非法或不適當的時間調用方法時產生的信號。換句話說,即 Java 環境或 Java 應用程序沒有處於請求操作所要求的適當狀態下。 |
| IllegalThreadStateException | 線程沒有處於請求操作所要求的適當狀態時拋出的異常。 |
| IndexOutOfBoundsException | 指示某排序索引(例如對數組、字符串或向量的排序)超出范圍時拋出。 |
| NegativeArraySizeException | 如果應用程序試圖創建大小為負的數組,則拋出該異常。 |
| NullPointerException | 當應用程序試圖在需要對象的地方使用 null 時,拋出該異常 |
| NumberFormatException | 當應用程序試圖將字符串轉換成一種數值類型,但該字符串不能轉換為適當格式時,拋出該異常。 |
| StringIndexOutOfBoundsException | 此異常由 String 方法拋出,指示索引或者為負,或者超出字符串的大小。 |
| … | … |
檢測性異常:
| 異常 | 描述 |
|---|---|
| ClassNotFoundException | 應用程序試圖加載類時,找不到相應的類,拋出該異常。 |
| CloneNotSupportedException | 當調用 Object 類中的 clone 方法克隆對象,但該對象的類無法實現 Cloneable 接口時,拋出該異常。 |
| IllegalAccessException | 拒絕訪問一個類的時候,拋出該異常。 |
| InstantiationException | 當試圖使用 Class 類中的 newInstance 方法創建一個類的實例,而指定的類對象因為是一個接口或是一個抽象類而無法實例化時,拋出該異常。 |
| InterruptedException | 一個線程被另一個線程中斷,拋出該異常。 |
| NoSuchFieldException | 請求的變量不存在 |
| NoSuchMethodException | 請求的方法不存在 |
| IOException及其子類 | 對文件或流的操作有誤時,拋出異常 |
| … | … |
實現代碼
package step2;
import java.util.Scanner;
public class Task {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int num1 = sc.nextInt();
int num2 = sc.nextInt();
/********* Begin *********/
try{
System.out.println(num1/num2);
}catch(ArithmeticException e){
System.out.println("除數不能為0");
}
/********* End *********/
}
}
第3關:拋出異常
任務描述
本關任務:拋出程序的異常。
相關知識
為了完成本關任務,你需要掌握:1. 如何使用throw和throws關鍵字。
throws 關鍵字
上一小節我們學習了如何處理捕獲異常,但是我們有時候想偷個懶,不自己處理這些異常,可不可以呢?
答案是可以的!
我們可以將自己不想處理的異常交給別人來處理,怎么實現呢?
很簡單我們只需要用throws關鍵字拋出該異常即可。
例如:

這個時候我們發現IDE報錯了,提示我們這里有異常要處理,但是如果我們不想處理,就可以這樣:

可以發現test方法內部沒有報錯了,但是調用test方法的地方報錯了。是什么原因呢?
拋出異常,就像我們平常所說的“甩鍋”,總有一個人要背鍋,在Java中也一樣異常最終總是要被處理或者被捕獲的,所以我們如果在方法的括號后面拋出一個異常,那么該方法的調用者是必須要捕獲這個異常或者將這個異常繼續拋出的。
所以上面的錯誤就會有兩種解決辦法。
第一種:捕獲異常:(使用try-catch)

第二種:把“鍋”甩給Java虛擬機:(繼續在調用者方法名的()后面寫throws,即繼續拋出異常)

throw 關鍵字
throw關鍵字的作用是:主動拋出異常;
首先我們來看系統自動拋出異常:
public static void main(String[] args) {
int a = 10;
int b = 0;
System.out.println(a/b);
}
運行這段代碼系統會自動拋出,java.lang.ArithmeticException異常。
這段程序使用throw關鍵字也可以實現:
public static void main(String[] args) {
int a = 10;
int b = 0;
if(b == 0){
throw new ArithmeticException("/ by zero");
}
System.out.println(a/b);
}
可以發現兩段程序的運行結果都類似:

throw是語句拋出一個異常,一般是在代碼塊的內部(即在方法體內),當程序出現某種邏輯錯誤時由程序員主動拋出某種特定類型的異常。
注意:使用throw關鍵字主動拋出檢測性異常的時候,在方法名上必須使用throws表明調用這個方法可能存在要拋出的異常。
舉個例子:


ArithmeticException屬於運行時異常,是在運行時檢測的,所以上述代碼編譯是能通過的,而FileNotFoundException是屬於檢測性異常,是在編譯之前就需要處理的,所以第二段程序要加上throws才能通過編譯。

編程要求
請仔細閱讀右側代碼,根據方法內的提示,在Begin - End區域內進行代碼補充,具體任務如下:
- 異常的拋出和處理。
測試說明
補充完代碼后,點擊測評,平台會對你編寫的代碼進行測試,當你的結果與預期輸出一致時,即為通過。
預期輸出:該文件不存在。
實現代碼
package step3;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class Task {
/********* Begin *********/
//請在合適的部位添加代碼
public static void main(String[] args) throws FileNotFoundException {
test();
}
public static void test() throws FileNotFoundException {
File file = new File("abc");
if(!file.exists()){ //判斷文件是否存在
//文件不存在,則 拋出 文件不存在異常
throw new FileNotFoundException("該文件不存在"); //輸出內容直接寫在異常的括號里面
}else{
FileInputStream fs = new FileInputStream(file);
}
}
/********* End *********/
}
實驗分析
- 在使用多個catch語句捕獲try語句塊中的代碼拋出異常時,需要注意catch語句的順序。若多個catch語句所要捕獲的異常類之間具有繼承關系,則用來捕獲子類的catch語句要放在捕獲父類的catch語句的前面
- 若不知代碼拋出的是哪種異常,可指定他們的父類Exception
- throw用於方法體內,並且拋出一個異常類對象,而throws用在方法聲明中來指明方法可能拋出的多個異常
throw new FileNotFoundException("該文件不存在"); //輸出內容直接寫在異常的括號里面- Java語言內置的異常類都繼承自java.lang.Throwable類
第4關:自定義異常
任務描述
本關任務:定義和使用自定義異常。
相關知識
為了完成本關任務,你需要掌握:
- 為什么要自定義異常;
- 怎么使用自定義異常。
自定義異常
前面談到的都是系統自帶的異常,但是如果我們是在開發一個復雜項目,就經常會遇到系統自帶的異常不能滿足我們的需求的情況,所以這個時候就需要我們自己來定義異常了。
使用自定義異常
我們一般使用繼承Exception類的方式來自定義異常,那具體怎么進行呢?
很簡單,我們只需要繼承Exception,再將信息傳遞給父類就可以了:
class 自定義異常名 extends Exception{
//因為Exception已經實現了很多異常處理的方法了屬性了,
//所以自定義異常只需要將信息傳遞給父類(使用super關鍵字)即可
}
一個簡單的自定義異常:

輸出:我是自定義異常
這樣就實現了一個自定義異常的定義和使用啦。
編程要求
請仔細閱讀右側代碼,根據方法內的提示,在Begin - End區域內進行代碼補充,具體任務如下:
- 定義一個自定義異常,判斷用戶名是否小於三位,如果用戶名小於三位,就拋出一個自定義異常。
測試說明
補充完代碼后,點擊測評,平台會對你編寫的代碼進行測試,當你的結果與預期輸出一致時,即為通過。
輸入:
admin
輸出:
用戶名格式正確
輸入:
ab
輸出:
Exception in thread "main" step4.MyException: 用戶名小於三位Exception at step4.Task.main(Task.java:13)
注意:因為拋出異常時會顯示行號,請在13行拋出異常,否則評測可能不通過。
實現代碼
package step4;
import java.util.Scanner;
public class Task {
/********* Begin *********/
public static void main(String[] args) throws MyException { //1.throws拋出給jvm
Scanner sc = new Scanner(System.in);
String username = sc.next();
//判斷用戶名
if(username.length()<3){
throw new MyException("用戶名小於三位Exception"); //2.throw,參數為輸出內容,要用new
}else{
System.out.println("用戶名格式正確");
}
}
}
class MyException extends Exception{
public MyException(String username){ //3.自定義異常(是一種類)的構造方法
super(username); //4.使用super()
}
}
/********* End *********/
實驗分析
Exception in thread "main" step4.MyException: 用戶名小於三位Exception at step4.Task.main(Task.java:13),出現異常,需要輸出的內容為用戶名小於三位Exception
