文章目錄
- Java基礎語法(十)—— 認識例外
- 一、初識例外
- 1.算數例外
- 2.陣列下標越界例外
- 3.空指標例外
- 4.例外的處理方式
- 5.例外的好處
- 二、例外的基本語法
- 1.基本格式
- 2. 是否處理例外對程式的影響
- 3.用 try ... catch 需要注意的問題
- 4.關于例外的處理方式
- 5.finally 的使用
- 6.例外處理流程
- 7.拋出例外
- (1)throw 的使用
- (2)宣告例外
- (3)小結
- 三、Java例外體系
- 四、自定義例外類
- 完!
Java基礎語法(十)—— 認識例外
接上篇博客 Java基礎語法(九)——String 類
本次內容介紹大綱

一、初識例外
??經常有同學看到例外來問了,例外到底是什么? 而在我們之前的學習中,我們其實已經接觸到了Java當中的例外,
1.算數例外
??首先我們遇到的第一個例外是我們在講除號運算子時遇到的——算數例外,
見以下代碼

我們在分子的位置出現了0,來運行以上代碼,
運行結果:

2.陣列下標越界例外
在陣列篇我們也提到了陣列越界的問題
我們來看以下代碼:

運行時結果:

查看例外的資訊

3.空指標例外
空指標例外也在之前的學習中經常出現
看以下代碼

??我們將 array 陣列置為 null ,之后再去訪問這個陣列,就出現了空指標例外,
運行結果:

??這些例外都是需要我們在平時所積累出來的,
??所謂例外指的就是程式在 運行時 出現錯誤時通知呼叫者的一種機制.
關鍵字 “運行時”
??有些錯誤是這樣的, 例如將 System.out.println 拼寫錯了, 寫成了 system.out.println. 此時編譯程序中就會出錯, 這是 “編譯期” 出錯.
??而運行時指的是程式已經編譯通過得到 class 檔案了, 再由 JVM 執行程序中出現的錯誤.
4.例外的處理方式
??例外的種類有很多, 不同種類的例外具有不同的含義, 也有不同的處理方式.(在此了解即可)
防御式編程
??錯誤在代碼中是客觀存在的. 因此我們要讓程式出現問題的時候及時通知程式猿. 我們有兩種主要的方式
LBYL: Look Before You Leap.
在操作之前就做充分的檢查.
EAFP: It’s Easier to Ask Forgiveness than Permission.
“事后獲取原諒比事前獲取許可更容易”. 也就是先操作, 遇到問題再處理.
其實很好理解,打一個非常形象的比喻來理解啊:
比如說有一個你非常喜歡的女生,你想要去拉她的手,那么有幾種方式呢?
第一種方式 LBYL:問一下:我能拉你的手嗎?
這就是操作之前仔細檢查,
第二種方式 EAFP:直接先把手拉上,她要是甩開了再說其他的,要是沒拒絕就拉著唄,
這就是”事后獲取原諒比事前獲取許可更容易“. 也就是先操作, 遇到問題再處理.
了解即可,不用特別去記憶,
5.例外的好處
我們看一下,上述的兩種風格在處理代碼時究竟是怎樣的呢?
我們先給一個特定的場景啊,處理王者榮耀游戲開局時的例外代碼
LBYL 風格的代碼(不使用例外)
??代碼的每一步執行完都要進行檢查,確認正確才能進行下一步,這就是在操作之前做檢查,
EAFP 風格的代碼
在這里我們能體會到Java 風格的代碼例外處理

??對比兩種不同風格的代碼, 我們可以發現, 使用第一種方式, 正常流程和錯誤處理流程代碼混在一起, 代碼整體顯的比較混亂. 而第二種方式正常流程和錯誤流程是分離開的, 更容易理解代碼.
二、例外的基本語法
??接下來,我們就正式開始了 Java當中 處理例外的 基本語法講解了,
1.基本格式
Java當中 處理例外的基本格式

2. 是否處理例外對程式的影響
我們來看一組代碼:

??這組代碼中,在代碼執行的第二步中,我們存在著陣列越界例外,那么第三步的 “hello” 是否會列印呢?
我們運行程式,看結果

??“hello” 并沒有列印,那么這是為什么呢?此時程式出現例外了,而當程式出現例外時,那么代碼將不會被執行,
??那么我們還是想執行這個“hello”,那么我們該怎么辦呢?
我們就將代碼寫成以下格式的代碼:

運行結果;

??hello 也成功進行了列印,也就是說,在這種方式下,代碼拋出例外,捕獲例外之后,代碼將繼續向后執行,
??那么又有同學說了,我們定義的那個 e 沒有用到啊,我們再來看 e 的用處

e.printStackTrace — 列印出現例外堆疊的追蹤
我們來看這時程式運行結果

??這時就把出現例外的堆疊的位置列印出來了,這就是 e 的一個作用吧
??那么又有同學來問了,為什么之前的 “hello”無法列印出來,之后的try…catch 捕捉例外之后能夠列印出來呢?

3.用 try … catch 需要注意的問題
1.??在 catch 塊中,一定要捕獲相對應的例外,如果程式拋出的例外在catch 塊當中,不能被捕獲,那么就會交給 JVM 處理,
看以下代碼:

??在catch 塊當中并沒有捕獲到 陣列越界例外,我們來看一下運行結果,

直接交給 JVM 處理,程式終止,不在向下執行,
2.可以通過catch 捕獲多個例外
我們可以在 try之后 跟上多個 catch 來捕獲例外,如以下代碼:

運行程式結果如下:

??我們知道,所有的例外都繼承于Exception,那么有人問了,我們可以直接捕獲一個Exception的例外嗎?
我們來試一下,
??我們知道捕捉例外的順序是按照代碼書寫的順序執行的,大家看一下這段代碼

當Exception 這個父類例外放在開頭,那么下面的捕捉例外則進行報錯,
??如果這樣寫,那么Exception 后面所有的例外都失效了,為什么呢,因為不管 try {} 里面是什么例外,都會在Exception 這一步進行捕獲到,后面的捕獲例外自然失效了,
??所以當我們在前面寫了 Exception 這個捕獲例外時,后面就不要在進行捕獲其他例外了,
??那么又有同學說了,我們為了省事,那么我們以后都捕獲 Exception 這個 例外不就好了?
??當然不行,在這種情況下,我們不能區分我們捕獲到的是什么例外,所以,不建議大家直接捕獲到一個 Exception 的例外,
3.??不建議大家直接捕獲一個 Exception 的例外
??有些同學想要省事,想要將兩個例外合并成一條進行捕捉,我們只需要將兩個例外用 | 進行連接即可,
給個示例:

看一下運行結果:

成功運行,
總結:
1.??在 catch 塊中,一定要捕獲相對應的例外,如果程式拋出的例外在catch 塊當中,不能被捕獲,那么就會交給 JVM 處理,
2.??可以通過catch 捕獲多個例外
3.??不建議大家直接捕獲一個 Exception 的例外
4.??可以用 | 同時處理兩個例外,如上例,
4.關于例外的處理方式
例外的種類有很多, 我們要根據不同的業務場景來決定.
對于比較嚴重的問題(例如和算錢相關的場景), 應該讓程式直接崩潰, 防止造成更嚴重的后果
對于不太嚴重的問題(大多數場景), 可以記錄錯誤日志, 并通過監控報警程式及時通知程式猿
對于可能會恢復的問題(和網路相關的場景), 可以嘗試進行重試.
??在我們當前的代碼中采取的是經過簡化的第二種方式. 我們記錄的錯誤日志是出現例外的方法呼叫資訊, 能很快速的讓我們找到出現例外的位置. 以后在實際作業中我們會采取更完備的方式來記錄例外資訊.
關于 “呼叫堆疊”
??方法之間是存在相互呼叫關系的, 這種呼叫關系我們可以用 “呼叫堆疊” 來描述. 在 JVM 中有一塊記憶體空間稱為 “虛擬機堆疊” 專門存盤方法之間的呼叫關系. 當代碼中出現例外的時候, 我們就可以使用 e.printStackTrace(); 的方式查看出現例外代碼的呼叫堆疊.
5.finally 的使用
我們再來看一下例外基礎語法的學習

我們看到了try…catch 之后還可以跟著 finally
那我們來說一下 finally 的特性
??不管 這個代碼 是否拋出例外,finally 的 內容都會被執行,所以 finally 經常來做一些善后的內容,比如:關閉資源
我們來看一下這一組代碼

考一下大家,這組代碼執行的結果是什么呢?
運行結果如下:

列印結果為2,這是為什么呢?
首先我們需要明確的一點是,finally 的內容一定會被執行,
我們分析一下:
??在try 塊當中,在列印 array[4]時出現例外,后面的return 陳述句就不再執行了,所以最后執行 finally 塊,回傳2.
我們再來看一個代碼示例:

這組代碼執行的結果是什么呢?
運行結果:

最后回傳了2.
這又再次明確了一點:
finally 的內容是一定會被執行的
finally 的使用 總結:
1.finally 塊當中的代碼終究會被執行的
2 .不建議在 finally 當中出現 return 陳述句.
6.例外處理流程

??好了,到現在,我們算是講清楚了 try…catch…finally 及例外處理的流程等問題,那么大家以后在寫代碼的程序中,一定要記得去使用 try…catch,不能一味的交給 JVM 來處理它,好了我們開始下一塊內容——拋出例外,
7.拋出例外
??除了 Java 內置的類會拋出一些例外之外, 程式猿也可以手動拋出某個例外. 使用 throw 關鍵字完成這個操作,
??throw 一般拋出一個你想要拋出的例外(或者自定義的例外)
(1)throw 的使用
我們來看代碼示例:

??我們用 throw new 了一個算數例外,為什么要 new 呢? 因為算數例外本身也是一個類,也要實體化,
我們來看運行時結果;

??成功的拋出例外了,但是這樣寫有一個不好的地方,我們拋出了一個例外但是呢,這個例外我們只是拋出了但是并未處理,所以最后程式出現例外后交給JVM處理,程式最后終止,
(2)宣告例外
??對于我們呼叫devide 方法的人來說啊,如果 devide 方法的內容很多,我們就看不出 devide 會拋出一個例外,那么為了讓呼叫devide 方法的人知道,我們呼叫這個方法會拋出這個例外,一般情況下,我們會給這個方法進行宣告例外,那么怎么宣告呢?
通過throws 宣告這個方法會拋出一個例外
下面我們來看代碼示例:

??方法呼叫者知道了呼叫該方法可能會拋出 算數例外,就用上了 try…catch 來捕獲例外,
運行結果:

(3)小結

三、Java例外體系
那么例外到底有多少種?要了解這個問題,我們就需要知道Java的例外體系了,

上圖并沒有將所有的例外都列舉出來,只是大概演示一下,
在Java當中,我們所看到的例外,其實也就是對應著一個類,
??由上圖中我們可以看到,整個Java 例外體系 都是繼承于頂層類 Throwable,那么 Throwable 就是所有例外、錯誤的父類,
對于Throwable 來說,直接繼承這個類的有兩個子類,
Error ( 錯誤 ) 和 Exception(例外).
我們來看一下 jdk_api 幫助手冊中對 Throwable 的解釋

我們這篇講的是 例外,怎么又出現一個 錯誤Error呢?
我們也來認識以下Error
比如說我們寫一個代碼:

運行之后出現以下結果:

我們來對比一下:

例外是以Exception 結尾的,而錯誤是以 Error 結尾的,
對于Error 來說——這種錯誤一定得由程式員自己解決,
而對于Exception 來說——例外時可以由程式自己解決的,
而例外又有以下劃分

??運行時例外 Runtime Exception就是我們上面提到的 算數例外、陣列越界例外、型別轉換例外等等,那么就有同學問了?
什么是運行時例外?
運行時例外就是在程式運行的時候拋出的例外
什么是編譯時例外?
編譯時例外就是在程式編譯時拋出的例外
如果一段代碼可能拋出 受查例外, 那么必須顯式進行處理.
顯式處理的方式有兩種:
a) 使用 try catch 包裹起來
b) 在方法上加上例外說明, 相當于將處理動作交給上級呼叫者
別忘了 IDEA 神奇的 alt + enter, 能夠快速修正代碼.
小結:

四、自定義例外類
??Java 中雖然已經內置了豐富的例外類, 但是我們實際場景中可能還有一些情況需要我們對例外類進行擴展, 創建符合我們實際情況的例外.
我們來實作一個簡單的自定義的例外類
??首先 我們要自定義一個例外類 同時繼承一個父類例外

那么這個我們自定義的例外怎么用呢?
下面我們來看

運行結果:

這就是我們自定義例外的使用,
我們再來一個代碼示例
下面我們給一個真實的業務場景
例如, 我們實作一個用戶登陸功能.

??此時我們在處理用戶名密碼錯誤的時候可能就需要拋出兩種例外. 我們可以基于已有的例外類進行擴展(繼承), 創建和我們業務相關的例外類.

此時我們的 login 代碼可以改成

自定義類的注意事項:
1.自定義例外通常會繼承自 Exception 或者 RuntimeException
2.繼承自 Exception 的例外默認是受查例外
3.繼承自 RuntimeException 的例外默認是非受查例外.
??好了今天的知識就分享到這里,希望大家多多練習,熟練掌握,感謝大家的欣賞與關注!!
謝謝欣賞!
完!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/286380.html
標籤:java
下一篇:漫畫:什么是JVM的垃圾回收?


