Java核心技術梳理-異常處理

来源:https://www.cnblogs.com/yuanqinnan/archive/2019/05/04/10810238.html
-Advertisement-
Play Games

一、引言 異常總是不可避免的,就算我們自身的代碼足夠優秀,但卻不能保證用戶都按照我們想法進行輸入,就算用戶按照我們的想法進行輸入,我們也不能保證操作系統穩定,另外還有網路環境等,不可控因素太多,異常也不可避免。 但我們可以通過異常處理機制讓程式有更好的容錯性和相容性,當程式出現異常時,系統自動生成E ...


一、引言

異常總是不可避免的,就算我們自身的代碼足夠優秀,但卻不能保證用戶都按照我們想法進行輸入,就算用戶按照我們的想法進行輸入,我們也不能保證操作系統穩定,另外還有網路環境等,不可控因素太多,異常也不可避免。

但我們可以通過異常處理機制讓程式有更好的容錯性和相容性,當程式出現異常時,系統自動生成Exception對象通知系統,從而將業務功能實現代碼和錯誤處理代碼分離。

異常處理已經成為衡量一門語言是否成熟的標誌之一,增加了異常處理機制後程式有更好的健壯性和容錯性。

二、異常處理機制

我們希望系統有一種機制能夠將錯誤處理代碼和正常實現代碼分離開,相當於一個if else,if中為正常實現代碼,而else為錯誤的處理。

2.1 try...catch 捕獲異常

我們按照上面的思路,java利用try catch來進行異常捕獲。

try{
    //業務代碼
}
catch(IOException ex){
    //錯誤處理    
}
catch(Exception ex){
    //錯誤處理代碼
}

當try塊代碼出錯時,系統生成一個異常對象,並將對象拋給運行環境,這個過程叫做拋出異常,運行環境接收到異常對象是,會尋找處理該異常對象的catch代碼塊,找到合適的catch塊,就將對象給其處理,如果找不到,則運行環境終止,程式也將退出。

2.2 異常類繼承體系

Java提供了豐富的異常類,這些異常類有嚴格的繼承關係

 

從這個圖可以看出異常主要分為兩類,Error與Exception,Error錯誤一般是指與虛擬機相關的問題,如系統崩潰、虛擬機錯誤,這些錯誤無法恢復或不可能捕獲,將導致應用程式崩潰,這些不需要我們去捕獲。

在捕獲異常時我們通常把Exception類放在最後,因為按照異常捕獲的機制,從上至下判斷該異常對象是否是catch中的異常類或其異常子類,一旦比較成功則用此catch進行處理。如果將Exception類放在前面,那麼就會進行直接進入其中,因為Exception類是所有異常類的父類,那排在它後面的異常類將永遠得不到執行的機會,這種機制我們稱為先小後大

2.3 多異常捕獲

Java 7 開始,一個catch塊中可以捕獲多種類型的異常:

public static void main(String[] args) {
    try {
        Integer a = Integer.parseInt(args[0]);
        Integer b = Integer.parseInt(args[1]);
        Integer c = a / b;
        System.out.println(c);

    } catch (IndexOutOfBoundsException | NumberFormatException
            | ArithmeticException ie) {
        //異常變數預設final,不能重新賦值
        ie = new ArithmeticException("text");

    } catch (Exception ex) {

    }
}

多個異常之間用豎線(|)隔開,並且異常變數預設final,不能重新賦值。

2.4 獲取異常信息

異常捕獲後我們想要查看異常信息,可以通過catch後的異常形參來獲得,常用的方法如下:

  • getMessage():返回異常的詳細描述字元串。

  • getStackTrace():返回異常跟蹤棧信息。

  • printStackTrace():將異常跟蹤棧信息按照標準格式輸出。

  • printStackTrace(PrintStream p):將異常跟蹤棧信息輸出到指定輸出流。

2.5 finally回收資源

在try裡面打開的一些物理連接,需要顯示的回收,但顯然在try裡面或者catch裡面進行回收是不行的,try中一旦出現異常,回收可能不會執行到,而catch更不行,因為在沒有異常時根本不會執行,於是異常處理機制提供了finally,finally塊代碼一定會被執行,即是try中執行了return語句也還是會執行。

try{
    //業務代碼
}catch(XXXException xx){
    //異常處理
}catch(XXXException xx){
    
}finally{
    //資源回收
}

在異常處理中,try是必須的,沒有try塊,後面的catch和finally沒有意義,catch和finally必須出現一個,finally塊必須是最後。

如果try塊中有return語句,則會先執行finally,然後再執行return語句,如果try塊中有exit語句,則不會執行finally,都直接退出虛擬機了當然不會再去執行。

2.6 自動關閉的try語句

每次都需要資源回收,顯得有些麻煩,Java還支持一種寫法,直接在try後申明資源,那麼就可以替換掉finally,但這個資源必須實現Closeable或者AutoCloseable介面

try (
        BufferedReader bufferedReader = new BufferedReader(new FileReader(""))
) {
    bufferedReader.read();
}

三、Checked異常與Runtime異常

Java的異常分為兩大類:Checked(可檢查)異常和Runtime(運行時)異常,所有的RuntimeException類及其子類的實例就是Runtime異常,其他的都是Checked異常。

對於Checked異常處理方式有兩種,一種是明確知道如何處理該異常,用try catch來捕獲異常,然後在catch中修複異常,一種是不知道如何處理,在定義方法時申明拋出異常。

Runtime異常無需顯示申明拋出,需要捕獲異常,就用try catch來實現。

3.1 使用throws聲明拋出異常

使用throws聲明的思路是:當前方法不知道如何處理這種類型的異常,則由上一級調用者處理,如果main方法也不知道如何處理,也可以使用throws拋給JVM,JVM的處理是,列印異常的跟蹤棧信息,並終止程式。

throws聲明拋出只能在方法簽名中使用,可以聲明拋出多個異常類,多個異常類用逗號隔開,如:

public static void main(String[] args) throws IOException {
    FileInputStream fileInputStream = new FileInputStream("");
}

申明瞭throws就不需要再使用try catch來捕獲異常了。

如果某段代碼中調用了一個帶throws聲明的方法,那麼必須用try catch來處理或者也帶throws聲明,如下例子:

public static void main(String[] args) {
    try {
        test();
    } catch (IOException e) {
        e.printStackTrace();
    }

}
public static void test () throws IOException{
    FileInputStream fileInputStream = new FileInputStream("");
}

這個時候要註意,子類方法聲明拋出的異常應該是父類方法聲明拋出異常的子類活相同,不允許比父類聲明拋出的異常多。

本人之前是C#開發,C#開發沒有Checked異常,個人覺得這個Checked異常有些繁瑣,它最大的作用仿佛是提醒開發處理異常,避免因為粗心而忘記處理異常,不知道是不是理解有誤。

四、使用throw拋出異常

程式出現錯誤,系統會拋出異常,有時候我們也想自行拋出異常,比如用戶未登錄,我們可能就直接拋出錯誤,這種自行拋出的異常一般都與業務相關,因為業務數據與既定不符,但是這種異常並不是一種錯誤,系統不會捕捉,就需要我們自行拋出。

使用throw語句進行異常拋出,拋出的不是一個異常類,而是一個異常實例,而且每次只能拋出一個:

if (user== null) {
    throw new Exception("用戶不存在");
}

這裡我們又要區分Checked異常與運行時異常,運行時異常申明非常簡單,直接拋出即可,而Checked異常又要像之前一樣,要麼使用try catch,要麼聲明throws

public static void main(String[] args) {
    try {
        //檢查時異常需要寫try catch
        test1();
    } catch (Exception e) {
        e.printStackTrace();
    }
    //運行時異常直接調用即可
    test2();

}
public static void test1() throws Exception {
    if (1 > 0) {
        throw new Exception("用戶不存在");
    }
}
public static void test2() {
    if (1 > 0) {
        throw new RuntimeException("用戶不存在");
    }
}

4.1 自定義異常類

為了讓錯誤更直觀的表達信息,有時候我們需要自定義異常, 自定義異常很簡單,只需要繼承Exception或者RuntimeException基類,一般會提供兩個構造器,一個空構造器,一個帶有描述信息的構造器:

public class GlobalException extends RuntimeException {

    //無參構造器
    public GlobalException() {
    }

    //帶有錯誤描述信息的構造器
    public GlobalException(String msg) {
        super(msg);
    }
}

4.2 異常鏈

在實際開發中,我們一般會分層開發,比較常用的是三層,表現出、業務邏輯層、資料庫訪問層,我們不會拋出資料庫異常給用戶,因為這些異常中有堆棧信息,很不安全,也非常的不友好。

通常,我們捕獲原始異常(可以寫入日誌),然後再拋出一個業務異常(通常是自定義的異常),這個業務異常可以提示用戶異常的原因:

public void update() throws GlobalException{
    try{

        //執行sql
    }

    catch (SQLException ex){
        //記錄日誌
        ...
            //拋出自定義錯誤
            throw new GlobalException("資料庫報錯");
    }
    catch (Exception ex){
        //記錄日誌
        ...
            throw new GlobalException("未知錯誤!");
    }
}

這種捕獲一個異常然後拋出另一個異常,並將原始信息保存起來的是一種典型的鏈式處理(責任鏈模式)。

五、異常處理規則

異常給系統帶來了健壯性和容錯性,但是使用異常處理並非如此簡單,我們還要註意性能和結構的優化,有些規則我們必須瞭解,而這些規則的主要目標是:

  • 程式代碼混亂最小化。

  • 捕獲並保留診斷信息。

  • 通知合適的人員

  • 採用合適的方式結束異常。

5.1 不要過度使用異常

什麼叫過度使用異常呢?有兩種情況,一是把異常和普通錯誤放在一起,使用異常來代替錯誤,什麼意思呢?就是對一些我們已知或可控的錯誤進行異常處理,如一些業務邏輯判斷,用戶的輸入等,並不是只有直接拋出異常這種選擇,我們可以直接通過業務處理進行錯誤返回,而不是拋出錯誤,拋出錯誤的效率要低一些,只有對外部的、不能確定和預知的運行時錯誤使用異常。

二就是使用異常來代替流程式控制制,異常處理機制的初衷是將不可以預期的錯誤和正常的業務代碼分離,不應該用異常來進行流程式控制制。

5.2 不要使用過大的try塊

不要把大量的業務代碼放在try中,大量的業務代碼意味著錯誤可能性也增大,也意味著一旦出錯,分析錯誤的複雜度也增加,而且try中包含大量業務,可能後面緊跟的catch塊也很多,我們會使用多個catch來捕獲錯誤,這樣代碼也很臃腫,應該儘量細分try,去分別捕獲並處理。

5.3 不要忽略捕獲到的異常

不要忽略異常,當我們捕獲到異常時,我們不要去忽略它,如果在catch中什麼也不做,那是一種恐怖的做法,因為這意味著出現了錯誤我們並不知道(極特殊的情況例外,比如:一些可重試的業務處理),最起碼的做法是列印錯誤日誌,更進一步看是否可以修複錯誤,或者向上拋出錯誤。


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

-Advertisement-
Play Games
更多相關文章
  • fastDSF介紹 FastDFS是用c語言編寫的一款開源的分散式文件系統,它是由淘寶資深架構師餘慶編寫並開源。FastDFS專為互聯網量身定製,充分考慮了冗餘備份、負載均衡、線性擴容等機制,並註重高可用、高性能等指標,使用FastDFS很容易搭建一套高性能的文件伺服器集群提供文件上傳、下載等服務。 ...
  • 物聯網架構_對AWS的Greengrass的認識與理解 一,前言: 這段時間有許多的收穫,分析,還有總結,其中包括新系統的設計與開發,以及其中新技術的踩坑等等等。 但是最近真的很忙,項目的推進,面試工作等,尤其五月份還有考試。所以,趕緊趁著五一假期有些空暇,先發一些東西。之後,有機會再對自己的素材( ...
  • mq常用的一些使用場景:1. 通過非同步方式對系統解耦;2. 增加系統的併發處理能力;3.系統日誌的處理;4.事件驅動業務的一些場景 ...
  • Python中創建包是比較方便的,只需要在當前目錄建立一個文件夾, 文件夾中包含一個__init__.py文件和若幹個模塊文件, 其中__init__.py可以是一個空文件,但還是建議將包中所有需要導出的變數放到__all__中,這樣可以確保包的介面清晰明瞭,易於使用。 Python中創建包是比較方 ...
  • 今天寫replace方法的時候的代碼如下: 本以為運行結果會是:I really like cats 出乎意料結果卻是原字元串 查了一下才得知python中string是不可變的 >>> help(str.replace)Help on method_descriptor:replace(...) ...
  • 1. 安裝 Maven 前的必須準備 需先安裝 Java 環境 下載合適的 JDK 配置 JDK 環境變數 JAVA_HOME:為 JDK 安裝目錄 Path:為 JDK/bin 目錄 測試是否成功:'javac version' 'java version',如顯示 Java 版本信息則表示 JD ...
  • 轉載請註明出處: "https://www.cnblogs.com/funnyzpc/p/10801474.html" 第二節內容概覽 + 迴圈(for、switch) + 多值返回 + defer & finally + init(go) 與預設構造函數(java) + 常量、靜態變數(const ...
  • 話不多說,一個是算時間的,還有一個是生成驗證碼的 驗證碼: ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...