#課后作業1:課件動手動腦及驗證內容解答
一、AboutException.java示例運行與異常處理基礎驗證
-
代碼示例(模擬)
public class AboutException {
public static void main(String[] args) {
// 演示被0除異常
int i = 1, j = 0, k;
try {
k = i / j; // 可能拋出ArithmeticException的代碼
} catch (ArithmeticException e) {
// 異常處理邏輯
System.out.println("捕獲到被0除異常:" + e.getMessage());
e.printStackTrace(); // 打印異常堆棧信息
} finally {
System.out.println("無論是否發生異常,finally塊都會執行");
}// 演示數組越界異常 int[] arr = {1, 2, 3}; try { System.out.println(arr[5]); // 數組下標越界,拋出ArrayIndexOutOfBoundsException } catch (ArrayIndexOutOfBoundsException e) { System.out.println("捕獲到數組越界異常:" + e.getMessage()); }}
} -
運行結果
捕獲到被0除異常:/ by zero
java.lang.ArithmeticException: / by zero
at AboutException.main(AboutException.java:6)
無論是否發生異常,finally塊都會執行
捕獲到數組越界異常:Index 5 out of bounds for length 3 -
驗證結論
- try塊包裹可能發生異常的代碼,當異常發生時,程序跳轉到對應catch塊執行處理邏輯;
- printStackTrace()方法可打印異常傳播路徑(方法調用堆棧),getMessage()方法可獲取異常的具體描述信息;
- finally塊無論是否發生異常,都會被執行,常用于資源釋放等“善后”操作。
二、assert語句與AssertionError驗證
- 代碼示例
import java.util.Arrays;
import java.util.List;
public class TestAssert {
public static void main(String[] args) {
List
int s = 0;
for (int n : ints) {
s += n;
}
assert s == 7; // 實際s=6,斷言失敗將拋出AssertionError
}
}
- 運行步驟與結果
- 步驟1:默認關閉assert功能運行(直接用java TestAssert命令),程序無任何輸出,斷言未生效;
- 步驟2:啟用assert功能運行(命令:java -ea TestAssert),運行結果如下:
Exception in thread "main" java.lang.AssertionError
at TestAssert.main(TestAssert.java:10)
- 驗證結論
- AssertionError屬于Error類的子類,是系統級錯誤,默認情況下JVM關閉assert功能;
- 需通過JVM參數“-ea”(enable assertions)啟用assert功能,斷言條件不滿足時才會拋出AssertionError。
三、整數與浮點數除以零差異驗證(基于javap反匯編)
- 測試代碼
// 整數除以零(拋出異常)
public class IntDivideByZero {
public static void main(String[] args) {
int i = 1, j = 0, k;
k = i / j;
}
}
// 浮點數除以零(不拋出異常)
public class DoubleDivideByZero {
public static void main(String[] args) {
double d1 = 100, d2 = 0, result;
result = d1 / d2;
System.out.println("浮點數除以零:" + result);
}
}
- javap反匯編步驟與結果
- 步驟1:編譯代碼(javac IntDivideByZero.java DoubleDivideByZero.java);
- 步驟2:反匯編查看字節碼(javap -c IntDivideByZero 與 javap -c DoubleDivideByZero);
- 關鍵字節碼差異:
- IntDivideByZero的main方法中,整數除法對應“idiv”字節碼指令;
- DoubleDivideByZero的main方法中,浮點數除法對應“ddiv”字節碼指令。
- 驗證結論
- JVM對“idiv”和“ddiv”指令采用不同處理策略:整數除以零違反數學邏輯,JVM拋出ArithmeticException;浮點數除以零在IEEE 754標準中定義為“Infinity”(無窮大),因此不拋出異常,直接返回該結果。
四、多層異常捕獲驗證(CatchWho.java與CatchWho2.java)
(一)CatchWho.java運行驗證
-
代碼示例(修正語法錯誤:ArrayIndex0utOfBoundsException應為ArrayIndexOutOfBoundsException)
public class CatchWho {
public static void main(String[] args) {
try {
try {
throw new ArrayIndexOutOfBoundsException();
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("ArrayIndexOutOfBoundsException/內層try-catch");
}
throw new ArithmeticException();
} catch (ArithmeticException e) {
System.out.println("發生ArithmeticException");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("ArrayIndexOutOfBoundsException/外層try-catch");
}
}
} -
運行結果
ArrayIndexOutOfBoundsException/內層try-catch
發生ArithmeticException -
驗證結論
- 內層try塊拋出的ArrayIndexOutOfBoundsException被內層catch塊捕獲并處理;
- 內層異常處理完成后,外層try塊繼續執行“throw new ArithmeticException()”,該異常被外層對應catch塊捕獲。
(二)CatchWho2.java運行驗證
-
代碼示例(修正語法錯誤)
public class CatchWho2 {
public static void main(String[] args) {
try {
try {
throw new ArrayIndexOutOfBoundsException();
} catch (ArithmeticException e) { // 內層catch塊類型與拋出異常不匹配
System.out.println("ArrayIndexOutOfBoundsException/內層try-catch");
}
throw new ArithmeticException();
} catch (ArithmeticException e) {
System.out.println("發生ArithmeticException");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("ArrayIndexOutOfBoundsException/外層try-catch");
}
}
} -
運行結果
ArrayIndexOutOfBoundsException/外層try-catch -
驗證結論
- 內層try塊拋出ArrayIndexOutOfBoundsException,但內層catch塊聲明捕獲ArithmeticException,異常無法被內層處理,向外層傳播;
- 外層catch塊中,ArrayIndexOutOfBoundsException對應的catch塊在前(注:原代碼中外層catch順序為ArithmeticException在前,此處需注意:若外層先捕獲ArithmeticException,ArrayIndexOutOfBoundsException仍會被后續catch塊捕獲),最終被外層對應catch塊處理;
- 內層未執行“throw new ArithmeticException()”,因此該異常未拋出。
五、finally執行時機與System.exit()影響驗證(EmbedFinally.java與SystemExitAndFinally.java)
(一)EmbedFinally.java(多層嵌套finally)
-
代碼示例
public class EmbedFinally {
public static void main(String[] args) {
try { // 外層try
System.out.println("進入外層try塊");
try { // 內層try
System.out.println("進入內層try塊");
throw new ArithmeticException("內層異常"); // 拋出異常
} catch (ArithmeticException e) {
System.out.println("內層catch:" + e.getMessage());
} finally {
System.out.println("內層finally塊執行");
}
} catch (Exception e) {
System.out.println("外層catch塊執行");
} finally {
System.out.println("外層finally塊執行");
}
}
} -
運行結果
進入外層try塊
進入內層try塊
內層catch:內層異常
內層finally塊執行
外層finally塊執行 -
驗證結論
- 多層嵌套finally時,執行順序與嵌套順序一致:先執行內層finally,再執行外層finally;
- 無論異常在哪個層級拋出,只要被捕獲,所有層級的finally都會執行。
(二)SystemExitAndFinally.java(System.exit()對finally的影響)
-
代碼示例
public class SystemExitAndFinally {
public static void main(String[] args) {
try {
System.out.println("進入try塊");
System.exit(0); // 終止JVM運行
} catch (Exception e) {
System.out.println("catch塊執行");
} finally {
System.out.println("finally塊執行");
}
}
} -
運行結果
進入try塊 -
驗證結論
- finally塊并非“絕對”執行:當執行System.exit(0)時,JVM直接終止,后續代碼(包括finally塊)不再執行;
- System.exit(int status)用于強制終止JVM,status為0表示正常終止,非0表示異常終止。
六、Java 7 try-with-resources特性驗證(自動釋放資源)
- 代碼示例(基于FileInputStream,需處理IOException)
import java.io.FileInputStream;
import java.io.IOException;
public class TryWithResourcesDemo {
public static void main(String[] args) {
// try-with-resources語法:括號內創建實現AutoCloseable接口的對象
try (FileInputStream fis = new FileInputStream("test.txt")) {
System.out.println("文件輸入流創建成功,可進行讀寫操作");
// 模擬文件操作(此處省略具體讀寫邏輯)
} catch (IOException e) {
System.out.println("捕獲IO異常:" + e.getMessage());
}
// 無需手動調用fis.close(),JVM自動調用
}
}
- 運行結果(分兩種情況)
- 情況1:test.txt文件存在,運行結果:
文件輸入流創建成功,可進行讀寫操作 - 情況2:test.txt文件不存在,運行結果:
捕獲IO異常:test.txt (系統找不到指定的文件)
- 驗證結論
- try-with-resources語法要求括號內的對象必須實現AutoCloseable接口(FileInputStream實現了該接口);
- 當程序離開try塊(正常執行完成或發生異常)時,JVM會自動調用AutoCloseable接口的close()方法釋放資源,無需在finally塊中手動調用,避免資源泄露;
- 若close()方法本身拋出異常,會被catch塊捕獲(與業務異常一并處理)。
七、受控異常(Checked Exception)驗證(TestThrows.java)
- 原錯誤代碼與編譯報錯
public class TestThrows {
public static void main(String[] args) {
FileInputStream fis = new FileInputStream("a.txt"); // 編譯報錯
}
}
- 編譯報錯原因:FileInputStream的構造方法聲明拋出FileNotFoundException(受控異常),該異常屬于直接派生自Exception的Checked Exception,必須顯式處理(捕獲或聲明拋出),否則編譯不通過。
- 修正代碼1(聲明拋出異常)
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class TestThrows {
// main方法聲明拋出FileNotFoundException,由JVM默認處理(打印異常信息并終止程序)
public static void main(String[] args) throws FileNotFoundException {
FileInputStream fis = new FileInputStream("a.txt");
}
}
- 修正代碼2(捕獲異常)
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class TestThrows {
public static void main(String[] args) {
try {
FileInputStream fis = new FileInputStream("a.txt");
} catch (FileNotFoundException e) {
System.out.println("捕獲文件未找到異常:" + e.getMessage());
} catch (IOException e) { // FileNotFoundException是IOException的子類,需放在前面捕獲
System.out.println("捕獲IO異常:" + e.getMessage());
}
}
}
- 驗證結論
- 受控異常(Checked Exception)直接派生自Exception,必須顯式處理:要么在方法上用throws聲明拋出,由調用者處理;要么用try-catch捕獲處理;
- 捕獲異常時,子類異常的catch塊必須放在父類異常catch塊之前(如FileNotFoundException的catch塊需在IOException之前),否則編譯報錯(父類異常會“遮蔽”子類異常,子類異常的catch塊無法執行)。
八、子類重寫方法拋出受控異常的限制驗證(OverrideThrows.java)
- 代碼示例(父類與子類)
import java.io.FileNotFoundException;
import java.io.IOException;
// 父類
class Parent {
// 父類方法聲明拋出IOException(受控異常)
public void readFile() throws IOException {
System.out.println("父類讀取文件方法");
}
}
// 子類1(合法:拋出父類異常的子類)
class Child1 extends Parent {
@Override
public void readFile() throws FileNotFoundException { // FileNotFoundException是IOException的子類
System.out.println("子類1讀取文件方法");
}
}
// 子類2(非法:拋出父類異常的父類,編譯報錯)
class Child2 extends Parent {
@Override
// 編譯報錯:子類方法拋出的異常不能是父類方法拋出異常的父類
public void readFile() throws Exception {
System.out.println("子類2讀取文件方法");
}
}
- 編譯結果
- Child1編譯通過:子類重寫方法拋出的異常(FileNotFoundException)是父類方法拋出異常(IOException)的子類,符合規則;
- Child2編譯報錯:子類重寫方法拋出的異常(Exception)是父類方法拋出異常(IOException)的父類,違反“子類拋出受控異常不能超出父類異常范圍”的規則。
- 驗證結論
- 子類重寫父類方法時,若父類方法聲明拋出受控異常,子類方法拋出的受控異常必須是父類異常的子類或相同異常,不能是父類異常的父類;
- 子類方法可選擇不拋出任何異常(即使父類方法拋出異常),但不能拋出比父類方法范圍更廣的受控異常。
浙公網安備 33010602011771號