一、java異常體系
先看Java異常體系圖:
所有異常類的父類為Throwable類,兩個直接子類為Error和Exception分別表示錯誤和異常。
1、Error類
Error是程序無法處理的錯誤,它是由JVM產生和拋出的,比如OutOfMemoryError、ThreadDeath等。這些異常發生時,Java虛擬機(JVM)一般會選擇線程終止。
2、Exception類
Exception是程序本身可以處理的異常,這種異常分兩大類運行時異常(Unchecked Exception)和非運行時異常(Checked Exception)。程序中應當盡可能去處理這些異常。
RuntimeException(Unchecked Exception):即可以編譯通過,一般由程序的邏輯錯誤引起,開發過程中應盡量避免。例如:NullPointerException,IndexOutOfBoundsException等。自定義異常一般繼承此類。
RuntimeException以外的異常(checked Exception):編譯器在編譯階段進行處理,程序必須處理此類異常否則無法通過編譯,如IOException、SQLException等。
二、異常的捕獲和處理
異常的捕獲通過try、catch、finally三個關鍵字,三個語句塊均不能單獨使用,三者可以組成 try...catch...finally、try...catch、try...finally三種結構,catch語句可以有一個或多個,finally語句只能一個
1、普通無異常:
public class TestDemo { public static void main(String[] args) { String result = GetString(); System.out.print(result); } public static String GetString(){ try { System.out.print("GetString():try\n"); return "GetString()返回值\n"; } catch (Exception e) { System.out.print("GetString():catch\n"); return "GetString()返回值"; } finally { System.out.print("GetString():finally\n"); } //return "這里是方法底部返回值"; } }
運行結果:
整個執行順序如下:執行順序是try=>finally=>return
2、出現異常調用情況
public class TestDemo { public static void main(String[] args) { String result = GetString(); System.out.print(result); } public static String GetString(){ try { System.out.print("GetString():try\n"); int i = 0; int var = 1 / i ; System.out.print("執行GetString():1/i\n"); return "GetString()try 返回值\n"; } catch (Exception e) { System.out.print("GetString():catch\n"); return "GetString()catch 返回值"; } finally { System.out.print("GetString():finally\n"); } //return "這里是方法底部返回值"; } }
執行結果:
整個執行順序如下:try=>catch=>finally=>catch return
可以確定:
1)不論出不出現異常都會執行finally代碼塊
2)在try模塊中出現異常后,異常代碼后續流程都不會繼續執行
3、try、catch、finally模塊中進行賦值
public class TestDemo { public static void main(String[] args) { String result = GetString(); System.out.print(result); } public static String GetString(){ String var = ""; try { var = "try var值"; System.out.print("執行GetString():var\n"); return var; } catch (Exception e) { System.out.print("GetString():catch\n"); return "GetString()catch 返回值"; } finally { var = "finally var值"; System.out.print("GetString():finally\n"); } } }
執行結果:
通過執行結果可以看到,在finally中對var進行賦值,雖然程序執行了finally模塊,但是最終不會影響var的值,var的值還是在try模塊中賦值內容。
結論:雖然finally方法會被執行但是返回結果不會被改變,也就是finally是在return之后執行的,try模塊會先把返回結果先保存起來,然后不管finally代碼執行了什么,都不會影響到返回結果,等finally執行完成在返回結果。
三、業務如何定義異常
業務中需要按照一定的規范來定義異常,這樣有利於系統管理業務異常。
通過上面繼承圖可以看出,所有service都會繼承BaseExcepion,BaseException最終通過RuntimeException來實現。
而在實際的業務中需要准確定位異常歸屬的服務和具體類型,這樣就可以在對異常日志進行監控時有區分的進行不同的處理(比如重要的帳務或者渠道服務進行較高優先級的提醒或者自處理)。
所以可以在BaseException通過系統定義的code,code通過兩個部分來組成:不同的service定義的值和同一個service中具體異常類型值。
public class BaseException extends RuntimeException { private String code; public BusinessException() { super(); } public BusinessException(String code, String message) { super(message); this.code = code; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } /** * 獲取異常編碼 * @param sysCode 系統編號枚舉 * @param exceptionType 異常類型枚舉 * @param exceptionCode 異常順序碼 * @return String 異常編碼 */ protected static String getExceptionCode(SysCode sysCode, ExceptionType exceptionType, String exceptionCode) { if (StringUtil.isEmpty(exceptionCode) || StringUtil.length(exceptionCode) != 4) { throw new BusinessException("00000000", "異常編碼不符規范"); } return new StringBuilder(sysCode.getCode()).append(exceptionType.getCode()).append(exceptionCode).toString(); } public BusinessException print(String message) { logger.error("BusinessException Code:{};Message:{};OtherMessage:{}", this.code, super.getMessage(), message); return this; }
在serviceA中通過調用定義異常類型ORDER_PARAM_NULL,本身ServiceA自帶code:SysCode.SER_A,再和定義的1001進行拼接得到最終的該異常在整個系統中的異常code。
public class ServiceAException extends BusinessException { public static FeeException ORDER_PARAM_NULL = new ServiceAException(getExceptionCode( "1001"), "關鍵參數為空"); public static String getExceptionCode(String exceptionCode) { return getExceptionCode(SysCode.SER_A/*這里定義serviceA的系統code值*/, exceptionCode); } }