java異常處理機制

来源:https://www.cnblogs.com/f-ck-need-u/archive/2017/12/28/8130361.html
-Advertisement-
Play Games

JAVA中的異常類都繼承自Throwable類,也就是說,這是異常類的根。Throwable類擴展了兩個類Error類和Exception類,Exception類又擴展了一個RuntimeException類。如下圖: Error:稱為錯誤,由Java虛擬機生成並拋出,這類錯誤一般是運行時系統內部的 ...


  1. java異常是Java提供的用於處理程式中錯誤的一種機制。
  2. 所謂錯誤是指程式在運行過程中發生的一些異常事件(如除數為0、數組下標越界、操作的文件不存在等)。
  3. Java程式在執行過程中如果出現異常事件,可以生成一個異常類對象,該異常對象封裝了異常事件的信息並將被提交給Java運行時系統,這個過程稱為拋出(throw)異常。
  4. 當Java運行時系統收到異常對象時,會尋找能處理這一異常的代碼並將對當前異常對象交給其處理,這一過程稱為異常捕獲(catch)。

JAVA中的異常類都繼承自Throwable類,也就是說,這是異常類的根。Throwable類擴展了兩個類Error類和Exception類,Exception類又擴展了一個RuntimeException類。如下圖:

  • Error:稱為錯誤,由Java虛擬機生成並拋出,這類錯誤一般是運行時系統內部的錯誤,無法被處理。
  • Exception:所有異常類的父類,其子類對應了各種各樣可能出現的異常事件,一般需要用戶顯式地聲明或捕獲。如文件類異常:FileNotFoundException,IOExecption。
  • RuntimeException:一種特殊的異常類,繼承自Exception類。如除數為0、數組下標越界等。這類異常產生比較頻繁,用戶一般不對其做捕獲處理,否則對程式的可讀性和運行效率影響很大,而是由系統自動檢測並將它們交給預設的異常處理程式進行處理。如ArithmeticException,ArrayIndexOutOfBoundException。

一般來說,出現RuntimeException異常表示的是代碼不合理而出現的問題。

  • 未檢查異常:Error錯誤和RuntimeException類的異常;
  • 已檢查異常:Exception類的異常但不包括RuntimeException類。

因此,在自定義異常類型時,大多數都直接繼承Exception類,偶爾可能繼承RuntimeException類,更偶爾的可能會繼承這些類的某些子類。

try-catch-finally結構和處理流程

使用try-catch結構捕捉異常,並設置捕捉到後的處理方式。還可以加上finally結構,這是可選結構,但卻表示try結構中必須會被執行的部分。

以下是try-catch-finally結構和處理過程的分析。

try {
  // 待捕捉測試的代碼1
  // 待捕捉測試的代碼2   (假設此為異常代碼,將拋出名為異常名2的異常)
  // 待捕捉測試的代碼3
} catch (異常名1 對象名e) {
  // 捕捉到異常名1的異常後,該做的處理代碼4
} catch (異常名2 對象名e) {
  // 捕捉到異常名2的異常後,該做的處理代碼5
} ... {
  //...
} finally {
  //一定會執行的代碼6
}

  //try結構外的代碼7

前提假設,在各代碼中沒有return子句。執行過程為:首先代碼1正常執行,到代碼2處拋出異常名2的異常,通過異常名匹配,將選擇第二個catch結構,於是將異常2封裝到對象名e中,並執行代碼5處理異常。catch部分處理完後,還有最後處理段finally,於是執行代碼6。出了finally後,還將執行代碼7。

註意,當代碼2出現異常後,代碼3不會執行。而finally則是無論是否真的捕捉到了異常、是否在catch段有return都會執行的代碼段。換句話說,finally段的代碼除了內部錯誤或外界影響都一定會執行就像下麵的例子中,即使catch使用了return,但finally還是會執行,只不過這個catch終止了try結構外的代碼。

例如,除數為0時會拋出ArithmeticException異常。try-catch捕捉它:

public class TEx {
    public static void main(String[] args) {
        try {
            System.out.println("[start]");
            System.out.println(2/0);
            System.out.println("[end]");
        } catch (ArithmeticException e) {
            System.out.println("[Catching]: " + e);
            return;
        } finally {
            System.out.println("[Finally]");
        }
        System.out.println("[out of try-catch]");
    }
}

在finally段中還可以繼續try-catch-finally,防止該段落的代碼再次拋出異常。

public class TEx {
    public static void main(String[] args) {
        try {
            System.out.println("[start]");
            System.out.println(2/0);
            System.out.println("[end]");
        } catch (ArithmeticException e) {
            System.out.println("[Catching]: " + e);
            return;
        } finally {
            try {
                System.out.println("[Finally-try-start]");
                System.out.println(3/0);
            } catch (ArithmeticException e) {
                System.out.println("[Finally-Catching]: " + e);
            }
        }
        System.out.println("[out of try-catch]");
    }
}

輸出異常信息

java中的異常都會封裝到對象中。異常對象中有幾個方法:

  • printStackTrace():輸出最詳細的信息,包括拋出異常的行號,異常信息以及異常原因。
  • getMessage():輸出異常信息。
  • getCause():輸出異常原因。

異常拋出過程和throw、throws關鍵字

throw關鍵字用於在某個語句處拋出一個異常,只要執行到這個語句就表示必定拋出異常。
throws關鍵字用於在方法處拋出一個或多個異常,這表示執行這個方法可能會拋出異常。

throw OBJECT;
throw new EXCEPTION("Message");
method() throws EXCEPTION1[,EXCEPTION2...] {}

對於Exception類(非RuntimeException)的異常即已檢查異常類,在調用時要麼進行捕捉,要麼繼續向上級拋出。這類錯誤產生和處理的過程為:

  1. 方法f()內部的方法體的throw向上拋出給方法f();
  2. 方法f()的throws向上拋出拋給f()調用者;
  3. 方法調用者必須捕捉處理,或者不想捕捉就繼續向上拋出;
  4. 每一級的調用者都不想捕捉而是一直向上拋出,則最後由java虛擬機報錯:"未報告的異常錯誤XXXX必須對其進行捕獲或聲明以便拋出"。

以下是拋出異常的一個簡單示例,拋出的是ArithmeticException異常,因為是RuntimeException類異常,因此從方法體內部throw拋出後,無需在方法定義處使用throws繼續拋出。

public class EX {
    void f(int n) {          // 或void f(int n) throws ArithmeticException {}
        if (n == 0) {
            throw new ArithmeticException("hello Exception!");
        } else {
            System.out.println("right!");
        }
    }

    public static void main(String[] args) {
            EX m = new EX();
            m.f(1);
            m.f(0);   // throw Exception
    }
}

執行結果:

right!
Exception in thread "main" java.lang.ArithmeticException: hello Exception!   //異常的信息
        at EX.f(EX.java:4)      //真實產生異常的地方
        at EX.main(EX.java:13)  //調用產生異常的地方

所以,對於RuntimeException類異常來說,是否使用throws關鍵字並無影響。一般來說,Exception類異常但非RuntimeException才需要使用throws關鍵字,因為Exception類異常必須要被捕獲並處理,而RuntimeException異常則無所謂。

例如將上面的ArimeticException改為FileNotFoundException,前者為Runtime類異常,而後者為Exception但非Runtime類異常,因此使用throw拋出後,必須在定義方法處也使用throws拋出錯誤。這一過程是"向上級拋出"的過程:"方法體內部拋出異常-->拋給方法本身"

void f(int n) throws FileNotFoundException {
    if (n == 0) {
        throw new FileNotFoundException("hello Exception!");  //throw a new yichang duixiang
    } else {
        System.out.println("right!");
    }
}

如果不使用throws關鍵字拋出錯誤,則將報錯:

EX.java:6: 錯誤: 未報告的異常錯誤FileNotFoundException; 必須對其進行捕獲或聲明以便拋出
       throw new FileNotFoundException("hello Exception!");  //throw a new yichang duixiang

從方法f()拋出向上拋出給調用者後,調用者要麼使用try-catch捕捉,要麼繼續向上拋出,否則報錯。例如捕捉

import java.io.*;

public class EX {
    void f(int n) throws FileNotFoundException {
        if (n == 0) {
            throw new FileNotFoundException("hello Exception!");
        } else {
            System.out.println("right!");
        }
    }

    public static void main(String[] args) {
      try {
          EX m = new EX();
          m.f(0);
      } catch (FileNotFoundException e) {
         System.out.println(e); 
      }
        System.out.println("out of try-catch");
    }
}

如果不捕捉,則可以繼續在方法處向上拋出:

    public static void main(String[] args) throws FileNotFoundException {
          EX m = new EX();
          m.f(0);
    }

拋出異常時的註意事項

throw可以同時定義可能拋出的多種異常,儘管這些異常存在繼承關係。這時在捕捉異常時,應該先捕捉子類,再捕捉父類。

例如FileNotFoundException是IOException的子類,可以同時:

throws FileNotFoundException,IOException

捕捉時應先捕捉FileNotFoundException,再IOException。

try {
...
} catch (FileNotFoundException e) {
...
} catch (IOException e) {
...
}

在重寫有throws子句的方法時,需要註意:

  1. 子類重寫父類方法要拋出與父類一致的異常,或者不拋出異常
  2. 子類重寫父類方法所拋出的Exception類異常不能超過父類的範疇
  3. 子類重寫父類方法拋出的異常可以超出父類範疇,但超出的部分必須是RuntimeException類的異常

所以下麵的定義中,前子類1-3重寫和子類5-7都是有效的,但子類4重寫是錯誤的。

父類:method() throws IOException {}
子類1:method() throws {}
子類2:method() throws IOException {}
子類3:method() throws FileNotFoundException {}
子類4:method() throws Exception {}
子類5:method() throws RuntimeException {}
子類6:method() throws IOException,RuntimeException {}
子類7:method() throws IOException,ArithmeticException {}

自定義異常

異常是類,當產生異常時會構建此類的異常對象。

自定義異常時需要考慮異常類的構造方法是否接參數,參數需要如何處理實現怎樣的邏輯。

自定義的異常一般都從Exception繼承。

例如下麵定義了一個銀行卡存、取錢時的程式,其中自定義了一個Exception類錯誤。

// User Define Exception
class OverDrawException extends Exception {
    private double amount;
    public OverDrawException(double amount) {
        this.amount = amount;
    }

    public OverDrawException(String message) {
        super(message);
    }

    public double getAmount() {
        return amount;
    }
}

// Card class
class Card {
    //cardNum:卡號,balance:餘額
    private String cardNum;
    private double balance;

    Card(String n,double b) {
        this.cardNum = n;
        this.balance = b;
    }

    //方法:存錢
    public void cunQian(double n) {
        balance += n;
    }

    //方法:取錢
    public void quQian(double n) throws OverDrawException {
        if (n <= this.balance) {
            balance -= n;
        } else {
            double need = n - balance;
            throw new OverDrawException(need);
        }
    }

    //方法:返回餘額
    public double getBalance() {
        return this.balance;
    }
}

public class SuanZhang {
    public static void main(String [] args) {
        try {
            Card card = new Card("62202",300);
            System.out.println("卡裡餘額:" + card.getBalance());
            //存錢
            card.cunQian(200);
            System.out.println("餘額:" + card.getBalance());
            //取錢
            card.quQian(600);
            System.out.println("餘額:" + card.getBalance());
        } catch (OverDrawException e) {
            System.out.println(e);
            System.out.println("抱歉!您的餘額不足,缺少:" + e.getAmount());
        }
    }
}

註:若您覺得這篇文章還不錯請點擊右下角推薦,您的支持能激發作者更大的寫作熱情,非常感謝!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近這幾天在幫 "檸檬" 看她的APM系統要如何收集.Net運行時的各種事件, 這些事件包括線程開始, JIT執行, GC觸發等等. .Net在windows上(NetFramework, CoreCLR)通過ETW(Event Tracing for Windows), 在linux上(CoreC ...
  • 1、安裝3個zookeeper 1.1創建集群安裝的目錄 1.2配置一個完整的服務 這裡不做詳細說明,參考我之前寫的 zookeeper單節點安裝 進行配置即可,此處直接複製之前單節點到集群目錄 創建數據文件目錄 在數據文件目錄下添加myid文件 從數字1開始 保存退出,查看是否添加成功 修改zk1 ...
  • 設計模式-模板方法模式(Template Method Pattern) 2.1 定義 定義一個操作中演算法的框架,將一些步驟延遲到子類中去操作,使得子類可以不改變結構就可以改變一些特定的步驟. 模板方法模式很簡單.就只是使用了一個繼承(extends),其中abstractClass 叫做抽象模板. ...
  • 一、無所不在的連接 針對不通的使用場景,無線網路技術有很多種。 鑒於無線網路技術如此多樣,籠統地概括所有無線網路的性能優化手段是不可能的。好在大多數無線技術的原理都是相通的,衡量性能的指標和約束條件也具有普遍實用性。只要把影響無線性能的基本原理搞清楚,那其他問題自然也就迎刃而解了。 二、無線網路的性 ...
  • 1、安裝jdk 2、安裝解壓zookeeper 先創建文件夾 解壓zookeeper壓縮包 3、 創建配置文件zoo.cfg 4、運行測試 ...
  • JEEPlatform 一款企業信息化開發基礎平臺,可以用於快速構建企業後臺管理系統,集成了OA(辦公自動化)、SCM(供應鏈系統)、ERP(企業資源管理系統)、CMS(內容管理系統)、CRM(客戶關係管理系統)等企業系統的通用業務功能。Github鏈接:https://github.com/u01 ...
  • 咖啡店需要做一個訂單系統,以合乎飲料供應要求。 1.最初是這樣設計的: 每一種飲料都需要繼承該抽象類,並覆寫cost()方法。 2.但是購買咖啡時需要考慮到調料的部分,每種咖啡會加不同種的調料,比如蒸奶、豆漿、摩卡或者覆蓋奶泡,那麼訂單系統需要考慮加入不同調料後的價格。因此需要實現不同的子類來定義添 ...
  • In Campaign mode, you can check your strategies on already defeated bases. You will not lose your troops.在戰役模式中,你能查看已被打敗的玩家的塔防策略,而且你不會損失任何戰鬥單位。Let's w ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...