異常機制
阿新 • • 發佈:2020-09-21
一、什麼是異常
- 實際工作中,遇到的情況不可能是非常完美的
- 比如:你寫的某個模組。使用者不一定符合你的要求,你的程式要開啟某個檔案,這個檔案可能不存在或檔案格式不對,你要讀取資料庫的資料,資料可能是空的。我們程式在跑著,記憶體或硬碟可能就滿了
- 然間程式在執行過程中,非常可能遇到剛剛提到的這些異 常問題,我們叫異常,英文名是Exception
- 意思是例外。這些例外情況,或者叫異常,怎麼讓我們寫的程式做出合理的處理,而不至於程式崩潰
- 異常指程式執行中出現的不期而至的各種情況,如:檔案找不到、網路連線失敗、非法引數等
- 異常發生在程式執行期間,它影響了正常的程式執行流程
1. 例子
- error
- a呼叫b,b呼叫a
package com.exception; public class Demo01 { public static void main(String[] args) { //匿名內部類 new Demo01().a(); } public void a(){ b(); } public void b(){ a(); } } /* Exception in thread "main" java.lang.StackOverflowError at com.exception.Demo01.b(Demo01.java:16) at com.exception.Demo01.a(Demo01.java:12) at com.exception.Demo01.b(Demo01.java:16) at com.exception.Demo01.a(Demo01.java:12) */
- 異常
- 0不能為被除數
public class Demo02 {
public static void main(String[] args) {
System.out.println(11/0);
}
}
/*
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.exception.Demo02.main(Demo02.java:5)
*/
2. 異常的簡單分類
-
要理解Java是如何工作的,需要掌握三種類型的異常
-
執行時異常——是由個人問題造成的
- 是可能被避免的異常,執行時異常可以在編譯時被忽略(與檢查性異常相反)
-
非執行時異常
-
檢查性異常
- 最具代表性的異常,是使用者錯誤或問題引起的異常
- 如果要開啟一個不存在的檔案,一個異常就發生了,這些異常在編譯時不能被簡單地忽略
- 最具代表性的異常,是使用者錯誤或問題引起的異常
-
錯誤ERROR
- 錯誤不是異常,是脫離程式設計師控制的問題,錯誤在程式碼中通常被忽略
- 例如,當棧溢位時,一個錯誤就發生了,他們在編譯也檢查不到
- 錯誤不是異常,是脫離程式設計師控制的問題,錯誤在程式碼中通常被忽略
-
-
二、異常體系結構
- Java把異常當作物件來處理,並定義了一個基類java.lang.Throwable作為所有異常的超類
- 在Java API中已經定義了許多異常類,這些異常分為兩大類,錯誤Error和異常Exception
1. 錯誤Error
- Error類物件由Java虛擬機器生成並丟擲,大多數錯誤程式碼編寫這所執行的操作無關
- Java虛擬機器執行錯誤,當JVM不在有繼續執行操作所需的記憶體資源時,將出現OutofMemoryError。這些異常發生錯誤是,Java虛擬機器(JVM)一般會選擇執行緒終止
- 還有發生在虛擬機器試圖執行應用時,如類定義錯誤(NoClassDefFoundError)、連結錯誤(LinkageError)這些錯誤是不可查的,因為它們在應用程式的控制和處理能力之外,而且絕大多數是程式執行時不允許出現的情況
2. 異常Exception
- 在Exception分支中有一個重要的子類RuntimeException(執行時異常)
- ArrayIndexOutBoundsException(陣列下標越界)
- NullPointerException(空指標異常)
- ArithmeticException(算數異常)
- MissingResourceException(丟失資源)
- ClassNotFoundException(找不到類)等異常,
- 這些異常時不檢查異常,程式中可以選擇捕獲處理,也可以不處理
- 這些異常一般是由程式邏輯錯誤引起的,程式應該從邏輯角度儘可能避免這些異常的發生
3. Error和Exception的區別
- Error通常是災難性的致命錯誤,是程式無法控制和處理的,當出現這些異常時,Java虛擬機器(JVM)一般會選擇終止執行緒
- Exception通常情況下是可以被程式處理的,並且在程式中應該儘可能的去處理這些異常
三、Java異常處理機制
- 丟擲異常
- 捕獲異常
- 異常處理五個關鍵字
- try、catch、finally、throw、throws
1. try、catch、final
- 異常型別——ArithmeticException
public class Test {
public static void main(String[] args) {
int a = 1;
int b = 0;
System.out.println(a/b);
}
}
/*
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.exception.Test.main(Test.java:8)
*/
- try監控區域
- catch 捕獲異常——想要捕獲的異常型別
- final處理善後——可以沒有,但是寫了一定會執行完final再報錯
- 假設IO流,資源scanner等的關閉
public class Test {
public static void main(String[] args) {
int a = 1;
int b = 0;
try {
System.out.println(a/b);
}catch (ArithmeticException e){//捕獲ArithmeticException
System.out.println("程式出現異常,變數b不能為0");
}finally {
System.out.println("finally");
}
}
}
/*
程式出現異常,變數b不能為0
finally
*/
1.1 final一定會執行
public class Test {
public static void main(String[] args) {
try {
//等價於new Test().a();
Test test = new Test();
test.a();
}catch (ArithmeticException e){
System.out.println("程式出現異常,變數b不能為0");
}finally {
System.out.println("finally");
}
}
public void a(){
b();
}
public void b(){
a();
}
}
/*
finally
Exception in thread "main" java.lang.StackOverflowError
at Test.b(Test.java:23)
at Test.a(Test.java:19)
at Test.b(Test.java:23)
at Test.a(Test.java:19)
*/
- 將捕獲類ArithmeticException改為捕獲類Throwable後
- 類Throwable包含類StackOverflowError
輸出結果
/*
程式出現異常,變數b不能為0
finally
*/
1.2 可以寫多個catch
- 要捕獲多個異常,從小到大
public class Test {
public static void main(String[] args) {
int a =1;
int b =0;
try {
System.out.println(a/b);
}catch (Error e){
System.out.println("Error");
}catch (Exception e){
System.out.println("Exception");
}catch (Throwable e){//最大的異常寫在最下面,否則會報錯,Throwable覆蓋Error和Exception
System.out.println("Throwable");
}finally {
System.out.println("finally");
}
}
public void a(){
b();
}
public void b(){
a();
}
}
1.3 快捷鍵Ctrl+Alt+L
- 自動生成try,catch
public class Test2 {
public static void main(String[] args) {
int a = 1;
int b = 0;
try {
System.out.println(a/b);
} catch (Exception e) {
System.exit(1);//可以自定義一些語句
e.printStackTrace();//預設,列印錯誤的棧資訊
} finally {
}
}
2. throw和throws
2.1 throw
- throw是語句丟擲一個異常
- 一般用在方法中
public class Test {
public static void main(String[] args) {
new Test().test(1,0);
}
public void test(int a,int b) {
System.out.println(a/b);
}
}
/*
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.exception.Test.test(Test.java:14)
at com.exception.Test.main(Test.java:6)
*/
- 算數異常屬於執行時異常,系統會自動丟擲,不需要主動丟擲
- 利用throw主動丟擲異常
package com.exception;
public class Test {
public static void main(String[] args) {
new Test().test(1,0);
}
public void test(int a,int b) {
if (b==0) {
throw new ArithmeticException();
}
System.out.println(a/b);
}
}
/*
Exception in thread "main" java.lang.ArithmeticException
at com.exception.Test.test(Test.java:12)
at com.exception.Test.main(Test.java:6)
*/
2.2 throws
- throws是方法可能丟擲異常的宣告
- 一般用在方法中
public class Test {
public static void main(String[] args) {
try {
new Test().test(1,0);
} catch (Exception e) {
e.printStackTrace();
}
}
public void test(int a,int b) throws ArithmeticException{
if (b==0) {
// throw new ArithmeticException();
System.out.println("throws");
}
}
}
2.3 throw和throws的區別
四、自定義異常
- 使用Java內建的異常類可以描述在程式設計時出現的大部分異常情況,除此之外,使用者還可以自定義異常,使用者自定義異常類,只需要繼承Exception類即可
- 在程式中使用自定義異常,大體可以分為以下幾個步驟
-
- 建立自定義異常類
- 在方法中通過throw關鍵詞丟擲異常物件
- 如果在當前丟擲異常的方法中處理異常,可以使用try-catch語句捕獲並處理;否則在方法的宣告處通過throws關鍵詞知名要跑出給方法呼叫者的異常,繼續進行下一步操作
- 在出現異常方法的呼叫者中捕獲並處理異常
-
1. 例子
- 自定義異常類MyException
//繼承了Exception後就是自定義異常類
public class MyException extends Exception{
//傳遞數字>10
private int detail;
//有參構造
public MyException(int a){
this.detail = a;
}
//toString:異常的列印資訊
@Override
public String toString() {
return "MyException{" +
"detail=" + detail +
'}';
}
}
- 測試類Test
public class Test {
//可能會存在異常的方法
public void test(int a) throws MyException{//選擇法2丟擲異常
System.out.println("傳遞的引數為"+a);
if (a>10) {
throw new MyException(a);//丟擲了一個異常
//解決方法1,把這個異常捕獲
//解決方法2. 把異常丟擲,拋到呼叫這個方法的人中捕獲它
}
System.out.println("OK");
}
}
- main方法
import com.exception.demo02.MyException;
import com.exception.demo02.Test;
public class Application {
public static void main(String[] args) {
Test test = new Test();
try {
test.test(11);
} catch (MyException e) {
//此處需要增加一些處理異常的程式碼
System.out.println("MyException=>"+e);
}
}
}
/*
**********************************
test.test(10);
**********************************
傳遞的引數為10
OK
**********************************
test.test(11);
**********************************
傳遞的引數為11
MyException=>MyException{detail=11}
*/
- 先自定義了一個異常類MyException
- 構造器初始化detail的值為 傳進來的值a
- 然後列印輸出一句話MyException=>MyException{detail=傳進來的值a}
- 在main中例項化測試類Test後
- 用try監控呼叫類Test物件test中的test方法
- 被呼叫test方法
- 當a的值<10時,則不會丟擲異常MyException
- 正常輸出內容
- 當a的值>10時,會丟擲一個異常MyException
- 用catch捕獲異常MyException,並處理解決異常
- 列印輸出一句MyException=>e
- e代表呼叫執行MyException這給類
- 列印輸出一句MyException=>e
- 用catch捕獲異常MyException,並處理解決異常
- 當a的值<10時,則不會丟擲異常MyException
- 被呼叫test方法
- 用try監控呼叫類Test物件test中的test方法
五、總結
- 處理執行時異常,採用邏輯去合理規避同時輔助try-catch處理
- 可避免程式卡住進行不下去
- 在多重catch塊後面,可以加上一個catch(Exception)來處理可能會被遺漏的異常
- 對於不確定的程式碼,也可以加上try-catch,處理潛在的異常
- 儘量去處理異常,不要只是簡單地呼叫printStckTrace()去列印輸出
- 增加一些處理異常的程式碼
- 具體如何處理異常,要根據不同的業務需求和異常型別去決定
- 儘量新增finally語句塊去釋放佔用資源
- Scanner