[03] 为什么要使用异常机制
生活随笔
收集整理的這篇文章主要介紹了
[03] 为什么要使用异常机制
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
因為代碼經驗和見識等原因,說實話現在對于異常的使用,我也算是理解甚少。為什么用?什么時候用?即便是在查閱了部分資料以后,也只能在這里提煉出部分自己能夠理解的,以供參考和討論。
1、使用異常的好處
1.1 隔離常規代碼和錯誤處理代碼
實際上,我們希望程序不要出現問題,用戶操作永遠邏輯清晰而正確,一切都按照我們祈禱的那樣運行,然而這是不可能的。必然會有錯誤必然會要我們去處理,但是錯誤的處理并不是我們代碼的核心。就像用戶取錢的操作,我們核心的代碼應該是賬戶金額變動和更新,而過程中可能出現的各種意外如余額不足,取錢超出額度等夾雜在我們的正常邏輯里,代碼必然顯得混亂,可讀性差。而異常機制將這些意外情況剝離了出來。
我們用個簡單的例子來說明:
//假如我們要實現將一個文件讀入內存,實際上真正核心只需要下面5步 readFile {open the file;determine its size;allocate that much memory;read the file into memory;close the file; }81//假如我們要實現將一個文件讀入內存,實際上真正核心只需要下面5步 ?2readFile {3 ? ?open the file;4 ? ?determine its size;5 ? ?allocate that much memory;6 ? ?read the file into memory;7 ? ?close the file;8}
//為了處理文件不能打開、不能確定文件大小、內存分配不足等可能出現的意外,我們可能最終寫成如下 errorCodeType readFile {initialize errorCode = 0;open the file;if (theFileIsOpen) {determine the length of the file;if (gotTheFileLength) {allocate that much memory;if (gotEnoughMemory) {read the file into memory;if (readFailed) {errorCode = -1;}} else {errorCode = -2;}} else {errorCode = -3;}close the file;if (theFileDidntClose && errorCode == 0) {errorCode = -4;} else {errorCode = errorCode and -4;}} else {errorCode = -5;}return errorCode; }311//為了處理文件不能打開、不能確定文件大小、內存分配不足等可能出現的意外,我們可能最終寫成如下2errorCodeType readFile {3 ? ?initialize errorCode = 0;4 ? 5 ? ?open the file;6 ? ?if (theFileIsOpen) {7 ? ? ? ?determine the length of the file;8 ? ? ? ?if (gotTheFileLength) {9 ? ? ? ? ? ?allocate that much memory;10 ? ? ? ? ? ?if (gotEnoughMemory) {11 ? ? ? ? ? ? ? ?read the file into memory;12 ? ? ? ? ? ? ? ?if (readFailed) {13 ? ? ? ? ? ? ? ? ? ?errorCode = -1;14 ? ? ? ? ? ? ? }15 ? ? ? ? ? } else {16 ? ? ? ? ? ? ? ?errorCode = -2;17 ? ? ? ? ? }18 ? ? ? } else {19 ? ? ? ? ? ?errorCode = -3;20 ? ? ? }21 ? ? ? ?close the file;22 ? ? ? ?if (theFileDidntClose && errorCode == 0) {23 ? ? ? ? ? ?errorCode = -4;24 ? ? ? } else {25 ? ? ? ? ? ?errorCode = errorCode and -4;26 ? ? ? }27 ? } else {28 ? ? ? ?errorCode = -5;29 ? }30 ? ?return errorCode;31}
如此我們得到的是混亂糟糕的代碼,可讀性極差,將來一旦出現需要維護的情況,更是苦不堪言。而當我們使用異常機制來處理時,清晰的處理邏輯和代碼可讀性不言而喻:readFile {try {open the file;determine its size;allocate that much memory;read the file into memory;close the file;} catch (fileOpenFailed) {doSomething;} catch (sizeDeterminationFailed) {doSomething;} catch (memoryAllocationFailed) {doSomething;} catch (readFailed) {doSomething;} catch (fileCloseFailed) {doSomething;} }191readFile {2 ? ?try {3 ? ? ? ?open the file;4 ? ? ? ?determine its size;5 ? ? ? ?allocate that much memory;6 ? ? ? ?read the file into memory;7 ? ? ? ?close the file;8 ? } catch (fileOpenFailed) {9 ? ? ?doSomething;10 ? } catch (sizeDeterminationFailed) {11 ? ? ? ?doSomething;12 ? } catch (memoryAllocationFailed) {13 ? ? ? ?doSomething;14 ? } catch (readFailed) {15 ? ? ? ?doSomething;16 ? } catch (fileCloseFailed) {17 ? ? ? ?doSomething;18 ? }19}
(筆者:如上例所有核心代碼都用try包裹,確實代碼清晰;而事實上倡導的是盡量減小try塊,我個人在寫一些涉及到IO的方法時,代碼經常因為這個原則被try-catch分割得七零八落,基本上和使用if-else無差,所以個人認為try塊對于代碼的粒度控制,不必完全基于盡量最小的原則,畢竟同時try-catch的性能影響微乎其微)
1.2 延遲處理
throws關鍵字的使用,使得可能出現的錯誤不必在當前邏輯中立即處理,而是留待給它的調用者來處理。因為很多時候,某些底層方法是不知道要如何去處理這些錯誤的,而只有業務層根據實際的業務邏輯和需求,才知道如何處理,比如業務層可能會將錯誤信息顯示給用戶,以起提示和引導操作。
實際上,也可以選擇層層拋出的方式,即“ catch語句中處理異常后,再次throw拋出該異常 ”,繼續拋出異常可使得調用方法能夠再次獲得并處理異常。比如程序員可以在底層方法中抓到異常后,打印錯誤日志以供開發者查看,同時再次拋出給上層調用者,以便業務層調用時使用,如顯示錯誤信息給用戶。
同時,受檢類型的異常也起到了提醒的作用,告知調用者這個方法可能發生異常,那么你必須進行捕獲并考慮處理。
1.3 異常的精確定位
com.test9.MyException: 文件沒有找到--02at com.test9.Test.g(Test.java:31)at com.test9.Test.main(Test.java:38) Caused by: com.test9.MyException: 文件沒有找到--01at com.test9.Test.f(Test.java:22)at com.test9.Test.g(Test.java:28)... 1 more Caused by: java.io.FileNotFoundException: G:\myfile\struts.txt (系統找不到指定的路徑。)at java.io.FileInputStream.open(Native Method)at java.io.FileInputStream.<init>(FileInputStream.java:106)at java.io.FileInputStream.<init>(FileInputStream.java:66)at java.io.FileReader.<init>(FileReader.java:41)at com.test9.Test.f(Test.java:17)... 2 more1com.test9.MyException: 文件沒有找到--022 ? ?at com.test9.Test.g(Test.java:31)3 ? ?at com.test9.Test.main(Test.java:38)4Caused by: com.test9.MyException: 文件沒有找到--015 ? ?at com.test9.Test.f(Test.java:22)6 ? ?at com.test9.Test.g(Test.java:28)7 ? ... 1 more8Caused by: java.io.FileNotFoundException: G:\myfile\struts.txt (系統找不到指定的路徑。)9 ? ?at java.io.FileInputStream.open(Native Method)10 ? ?at java.io.FileInputStream.<init>(FileInputStream.java:106)11 ? ?at java.io.FileInputStream.<init>(FileInputStream.java:66)12 ? ?at java.io.FileReader.<init>(FileReader.java:41)13 ? ?at com.test9.Test.f(Test.java:17)14 ? ... 2 more如果采用 if-else-print 的方式在控制臺打印錯誤信息,以達到出錯時提示的目的,那么在debug時無疑是不如異常機制的,如上圖例可以看到,使用異常機制,一旦出現異常,控制臺不光有提示,更精確定位了出錯的代碼位置。
編程5分鐘,找蟲2小時,善用異常機制能夠節約不少調試的時間。
2、異常使用的注意事項
- 異常捕獲后不做任何處理,就是耍流氓,挖坑埋自己
- 異常機制不要用來做流程或條件控制,因為處理效率較低
- try-catch若不觸發catch是不影響性能的,但是try塊仍然不要濫用包裹大量代碼 (詳見參考鏈接中相關文章)
- 方法出錯該拋異常就拋異常,而不是返回一些錯誤碼
3、參考鏈接
- 使用異常的優勢
- 如何正確使用Java異常處理機制
- 淺析Java異常機制
- Java上的try catch并不影響性能
- Java程序猿必須懂的一些異常處理指引
4、異常方面的教訓記錄
4.1 關于try-catch和自定義Exception
因為知道try塊不觸發異常并不影響性能,于是我個人為了代碼更好的可讀性,擴大了try塊的范圍。后來便給自己造成了一點麻煩的事就是,假如:A底層方法拋出異常,B調用A時會捕獲異常并打印信息,然后再次拋出該異常以供調用者使用。于是我在調用B時要求處理異常,為了便于異常信息的使用,我到B的代碼中去看是哪里拋出的異常,結果try塊太龐大幾乎包裹了所有代碼,以至于我無法判斷真正拋出異常的A在B中代碼的哪個部分。所以try塊也不能濫用。
另外,自定義異常的范圍不明確,這才以至于我需要追蹤到底層去判斷捕獲的異常如何處理,是內部記錄還是做彈窗給用戶提示?所以自定義異常的命名和創建也是需要明確范圍和目的,是哪種類型的業務就建相應的異常,而不要一股腦就單獨一個BusinessException。
轉載于:https://www.cnblogs.com/deng-cc/p/7462539.html
總結
以上是生活随笔為你收集整理的[03] 为什么要使用异常机制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 国际软件设计文档——概要设计说明书
- 下一篇: C++调试帮助