成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

瞧瞧別人家的異常處理,那叫一個優雅

開發 前端
在我們日常工作中,經常會遇到一些異常,比如:NullPointerException、NumberFormatException、ClassCastException等等。那么問題來了,我們該如何處理異常,讓代碼變得更優雅呢?

前言

在我們日常工作中,經常會遇到一些異常,比如:NullPointerException、NumberFormatException、ClassCastException等等。

那么問題來了,我們該如何處理異常,讓代碼變得更優雅呢?

1.不要忽略異常

不知道你有沒有遇到過下面這段代碼:

反例:

Long id = null;
try {
   id = Long.parseLong(keyword);
} catch(NumberFormatException e) {
  //忽略異常
}

用戶輸入的參數,使用Long.parseLong方法轉換成Long類型的過程中,如果出現了異常,則使用try/catch直接忽略了異常。

并且也沒有打印任何日志。

如果后面線上代碼出現了問題,有點不太好排查問題。

建議大家不要忽略異常,在后續的工作中,可能會帶來很多麻煩。

正例:

Long id = null;
try {
   id = Long.parseLong(keyword);
} catch(NumberFormatException e) {
  log.info(String.format("keyword:{} 轉換成Long類型失敗,原因:{}",keyword , e))
}

后面如果數據轉換出現問題,從日志中我們一眼就可以查到具體原因了。

2.使用全局異常處理器

有些小伙伴,經常喜歡在Service代碼中捕獲異常。

不管是普通異常Exception,還是運行時異常RuntimeException,都使用try/catch把它們捕獲。

反例:

try {
  checkParam(param);
} catch (BusinessException e) {
  return ApiResultUtil.error(1,"參數錯誤");
}

在每個Controller類中都捕獲異常。

在UserController、MenuController、RoleController、JobController等等,都有上面的這段代碼。

顯然這種做法會造成大量重復的代碼。

我們在Controller、Service等業務代碼中,盡可能少捕獲異常。

這種業務異常處理,應該交給攔截器統一處理。

在SpringBoot中可以使用@RestControllerAdvice注解,定義一個全局的異常處理handler,然后使用@ExceptionHandler注解在方法上處理異常。

例如:

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 統一處理異常
     *
     * @param e 異常
     * @return API請求響應實體
     */
    @ExceptionHandler(Exception.class)
    public ApiResult handleException(Exception e) {
        if (e instanceof BusinessException) {
            BusinessException businessException = (BusinessException) e;
            log.info("請求出現業務異常:", e);
            return ApiResultUtil.error(businessException.getCode(), businessException.getMessage());
        } 
        log.error("請求出現系統異常:", e);
        return ApiResultUtil.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), "服務器內部錯誤,請聯系系統管理員!");
    }

}

有了這個全局的異常處理器,之前我們在Controller或者Service中的try/catch代碼可以去掉。

如果在接口中出現異常,全局的異常處理器會幫我們封裝結果,返回給用戶。

3.盡可能捕獲具體異常

在你的業務邏輯方法中,有可能需要去處理多種不同的異常。

你可能你會覺得比較麻煩,而直接捕獲Exception。

反例:

try {
   doSomething();
} catch(Exception e) {
  log.error("doSomething處理失敗,原因:",e);
}

這樣捕獲異常太籠統了。

其實doSomething方法中,會拋出FileNotFoundException和IOException。

這種情況我們最好捕獲具體的異常,然后分別做處理。

正例:

try {
   doSomething();
} catch(FileNotFoundException e) {
  log.error("doSomething處理失敗,文件找不到,原因:",e);
} catch(IOException e) {
  log.error("doSomething處理失敗,IO出現了異常,原因:",e);
}

這樣如果后面出現了上面的異常,我們就非常方便知道是什么原因了。

4.在finally中關閉IO流

我們在使用IO流的時候,用完了之后,一般需要及時關閉,否則會浪費系統資源。

我們需要在try/catch中處理IO流,因為可能會出現IO異常。

反例:

try {
    File file = new File("/tmp/1.txt");
    FileInputStream fis = new FileInputStream(file);
    byte[] data = new byte[(int) file.length()];
    fis.read(data);
    for (byte b : data) {
        System.out.println(b);
    }
    fis.close();
} catch (IOException e) {
    log.error("讀取文件失敗,原因:",e)
}

上面的代碼直接在try的代碼塊中關閉fis。

假如在調用fis.read方法時,出現了IO異常,則可能會直接拋異常,進入catch代碼塊中,而此時fis.close方法沒辦法執行,也就是說這種情況下,無法正確關閉IO流。

正例:

FileInputStream fis = null;
try {
    File file = new File("/tmp/1.txt");
    fis = new FileInputStream(file);
    byte[] data = new byte[(int) file.length()];
    fis.read(data);
    for (byte b : data) {
        System.out.println(b);
    } 
} catch (IOException e) {
    log.error("讀取文件失敗,原因:",e)
} finally {
   if(fis != null) {
      try {
          fis.close();
          fis = null;
      } catch (IOException e) {
          log.error("讀取文件后關閉IO流失敗,原因:",e)
      }
   }
}

在finally代碼塊中關閉IO流。

但要先判斷fis不為空,否則在執行fis.close()方法時,可能會出現NullPointerException異常。

需要注意的地方時,在調用fis.close()方法時,也可能會拋異常,我們還需要進行try/catch處理。

5.多用try-catch-resource

前面在finally代碼塊中關閉IO流,還是覺得有點麻煩。

因此在JDK7之后,出現了一種新的語法糖try-with-resource。

上面的代碼可以改造成這樣的:

File file = new File("/tmp/1.txt");
try (FileInputStream fis = new FileInputStream(file)) {
    byte[] data = new byte[(int) file.length()];
    fis.read(data);
    for (byte b : data) {
        System.out.println(b);
    }
} catch (IOException e) {
    e.printStackTrace();
    log.error("讀取文件失敗,原因:",e)
}

try括號里頭的FileInputStream實現了一個AutoCloseable接口,所以無論這段代碼是正常執行完,還是有異常往外拋,還是內部代碼塊發生異常被截獲,最終都會自動關閉IO流。

我們盡量多用try-catch-resource的語法關閉IO流,可以少寫一些finally中的代碼。

而且在finally代碼塊中關閉IO流,有順序的問題,如果有多種IO,關閉的順序不對,可能會導致部分IO關閉失敗。

而try-catch-resource就沒有這個問題。

6.不在finally中return

我們在某個方法中,可能會有返回數據。

反例:

public int divide(int dividend, int divisor) {
    try {
        return dividend / divisor;
    } catch (ArithmeticException e) {
        // 異常處理
    } finally {
        return -1;
    }
}

上面的這個例子中,我們在finally代碼塊中返回了數據-1。

這樣最后在divide方法返回時,會將dividend / divisor的值覆蓋成-1,導致正常的結果也不對。

我們盡量不要在finally代碼塊中返回數據。

正解:

public int divide(int dividend, int divisor) {
    try {
        return dividend / divisor;
    } catch (ArithmeticException e) {
        // 異常處理
        return -1;
    }
}

如果dividend / divisor出現了異常,則在catch代碼塊中返回-1。

7.少用e.printStackTrace()

我們在本地開發中,喜歡使用e.printStackTrace()方法,將異常的堆棧跟蹤信息輸出到標準錯誤流中。

反例:

try {
   doSomething();
} catch(IOException e) {
  e.printStackTrace();
}

這種方式在本地確實容易定位問題。

但如果代碼部署到了生產環境,可能會帶來下面的問題:

  1. 可能會暴露敏感信息,如文件路徑、用戶名、密碼等。
  2. 可能會影響程序的性能和穩定性。

正解:

try {
   doSomething();
} catch(IOException e) {
  log.error("doSomething處理失敗,原因:",e);
}

我們要將異常信息記錄到日志中,而不是保留給用戶。

8.異常打印詳細一點

我們在捕獲了異常之后,需要把異常的相關信息記錄到日志當中。

反例:

try {
   double b = 1/0;
} catch(ArithmeticException e) {
    log.error("處理失敗,原因:",e.getMessage());
}

這個例子中使用e.getMessage()方法返回異常信息。

但執行結果為:

doSomething處理失敗,原因:

這種情況異常信息根本沒有打印出來。

我們應該把異常信息和堆棧都打印出來。

正例:

try {
   double b = 1/0;
} catch(ArithmeticException e) {
    log.error("處理失敗,原因:",e);
}

執行結果:

doSomething處理失敗,原因:
java.lang.ArithmeticException: / by zero
 at cn.net.susan.service.Test.main(Test.java:16)

將具體的異常,出現問題的代碼和具體行數都打印出來。

9.別捕獲了異常又馬上拋出

有時候,我們為了記錄日志,可能會對異常進行捕獲,然后又拋出。

反例:

try {
  doSomething();
} catch(ArithmeticException e) {
  log.error("doSomething處理失敗,原因:",e)
  throw e;
}

在調用doSomething方法時,如果出現了ArithmeticException異常,則先使用catch捕獲,記錄到日志中,然后使用throw關鍵拋出這個異常。

這個騷操作純屬是為了記錄日志。

但最后發現日志記錄兩次。

因為在后續的處理中,可能會將這個ArithmeticException異常又記錄一次。

這樣就會導致日志重復記錄了。

10.優先使用標準異常

在Java中已經定義了許多比較常用的標準異常,比如下面這張圖中列出的這些異常:

反例:

public void checkValue(int value) {
    if (value < 0) {
        throw new MyIllegalArgumentException("值不能為負");
    }
}

自定義了一個異常表示參數錯誤。

其實,我們可以直接復用已有的標準異常。

正例:

public void checkValue(int value) {
    if (value < 0) {
        throw new IllegalArgumentException("值不能為負");
    }
}

11.對異常進行文檔說明

我們在寫代碼的過程中,有一個好習慣是給方法、參數和返回值,增加文檔說明。

反例:

/*  
 *  處理用戶數據
 *  @param value 用戶輸入參數
 *  @return 值 
 */
public int doSomething(String value) 
     throws BusinessException {
     //業務邏輯
     return 1;
}

這個doSomething方法,把方法、參數、返回值都加了文檔說明,但異常沒有加。

正解:

/*  
 *  處理用戶數據
 *  @param value 用戶輸入參數
 *  @return 值
 *  @throws BusinessException 業務異常
 */
public int doSomething(String value) 
     throws BusinessException {
     //業務邏輯
     return 1;
}

拋出的異常,也需要增加文檔說明。

12.別用異常控制程序的流程

我們有時候,在程序中使用異常來控制了程序的流程,這種做法其實是不對的。

反例:

Long id = null;
try {
   id = Long.parseLong(idStr);
} catch(NumberFormatException e) {
   id = 1001;
}

如果用戶輸入的idStr是Long類型,則將它轉換成Long,然后賦值給id,否則id給默認值1001。

每次都需要try/catch還是比較影響系統性能的。

正例:

Long id = checkValueType(idStr) ? Long.parseLong(idStr) : 1001;

我們增加了一個checkValueType方法,判斷idStr的值,如果是Long類型,則直接轉換成Long,否則給默認值1001。

13.自定義異常

如果標準異常無法滿足我們的業務需求,我們可以自定義異常。

例如:

/**
 * 業務異常
 *
 * @author 蘇三
 * @date 2024/1/9 下午1:12
 */
@AllArgsConstructor
@Data
public class BusinessException extends RuntimeException {

    public static final long serialVersionUID = -6735897190745766939L;

    /**
     * 異常碼
     */
    private int code;

    /**
     * 具體異常信息
     */
    private String message;

    public BusinessException() {
        super();
    }

    public BusinessException(String message) {
        this.code = HttpStatus.INTERNAL_SERVER_ERROR.value();
        this.message = message;
    }
}

對于這種自定義的業務異常,我們可以增加code和message這兩個字段,code表示異常碼,而message表示具體的異常信息。

BusinessException繼承了RuntimeException運行時異常,后面處理起來更加靈活。

提供了多種構造方法。

定義了一個序列化ID(serialVersionUID)。

責任編輯:姜華 來源: 蘇三說技術
相關推薦

2025-04-22 08:20:51

2025-04-08 08:20:33

2024-11-12 08:20:31

2025-05-30 08:20:54

2022-12-12 08:14:47

2024-12-02 00:59:30

Spring

2025-03-06 08:21:02

判空entity對象

2025-02-28 08:21:00

2025-03-11 08:20:58

2020-11-03 16:00:33

API接口微服務框架編程語言

2017-11-12 21:32:52

戴爾

2015-09-24 09:22:16

nodejs頁面始末

2016-01-08 09:49:19

DockerDocker案例云應用開發

2020-11-17 09:34:31

API接口后端

2017-09-22 13:22:59

大數據南京大學宿舍

2021-07-14 06:31:08

京東互聯網加薪

2023-12-30 20:04:51

MyBatis框架數據

2021-01-20 05:42:27

RabbitMQMQ vhost

2017-06-13 14:15:51

戴爾協同計算汽車神話

2020-03-16 17:20:02

異常處理Spring Boot
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 欧美日韩大片 | 蜜桃视频在线观看免费视频网站www | 国产成人a亚洲精品 | 国产精品免费一区二区三区四区 | 欧美日韩在线观看一区二区三区 | 成人看片在线观看 | 青青草一区二区 | 亚洲一区二区三区在线播放 | 日韩精品在线播放 | 日韩精品一区二区三区四区视频 | 欧美国产日韩成人 | 青青久久久 | 午夜影晥 | 国产福利在线小视频 | 欧美日韩美女 | 69av在线视频 | 国产精品久久久久久久久免费 | 日韩一区二区三区在线 | 欧美激情亚洲 | 在线免费看毛片 | 精品一区二区久久久久久久网站 | 日本久草 | 亚洲欧美日韩久久久 | 97精品超碰一区二区三区 | 欧美日本韩国一区二区三区 | 韩日一区二区 | 日韩无| 久久久久久国产精品免费免费 | 欧美男人亚洲天堂 | 亚洲精品99久久久久久 | 亚洲乱码一区二区三区在线观看 | 97超碰成人 | 羞羞视频网 | chengrenzaixian| 久久亚洲一区 | 欧美一区二区三区视频 | 成人欧美一区二区三区在线播放 | 中文字幕日韩欧美一区二区三区 | 国产成人精品免高潮在线观看 | 欧美激情综合 | 视频一区二区三区在线观看 |