亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

“崩潰了?不可能,我全 Catch 住了” | Java 異常處理

stdying / 2934人閱讀

摘要:允許存在多個(gè),用于針對(duì)不同的異常做不同的處理。表示程序可能需要捕獲并且處理的異常。因此,我們應(yīng)該盡可能的避免通過(guò)異常來(lái)處理正常的邏輯檢查,這樣可以確保不會(huì)因?yàn)榘l(fā)生異常而導(dǎo)致性能問(wèn)題。異常表中的每一條記錄,都代表了一個(gè)異常處理器。

前言

今天我們來(lái)討論一下,程序中的錯(cuò)誤處理。

在任何一個(gè)穩(wěn)定的程序中,都會(huì)有大量的代碼在處理錯(cuò)誤,有一些業(yè)務(wù)錯(cuò)誤,我們可以通過(guò)主動(dòng)檢查判斷來(lái)規(guī)避,可對(duì)于一些不能主動(dòng)判斷的錯(cuò)誤,例如 RuntimeException,我們就需要使用 try-catch-finally 語(yǔ)句了。

有人說(shuō),錯(cuò)誤處理并不難啊,try-catch-finally 一把梭,try 放功能代碼,在 catch 中捕獲異常、處理異常,finally 中寫那些無(wú)論是否發(fā)生異常,都要執(zhí)行的代碼,這很簡(jiǎn)單啊。

處理錯(cuò)誤的代碼,確實(shí)并不難寫,可是想把錯(cuò)誤處理寫好,也并不是一件容易的事情。

接下來(lái)我們就從實(shí)現(xiàn)到 JVM 原理,講清楚 Java 的異常處理。

學(xué)東西,我還是推薦要帶著問(wèn)題去探索,提前思考幾個(gè)問(wèn)題吧:

一個(gè)方法,異常捕獲塊中,不同的地方的 return 語(yǔ)句,誰(shuí)會(huì)生效?

catch 和 finally 中出現(xiàn)異常,會(huì)如何處理?

try-catch 是否影響效率?

Java 異常捕獲的原理?

二、Java 異常處理 2.1 概述

既然是異常處理,肯定是區(qū)分異常發(fā)生捕獲、處理異常,這也正是組成異常處理的兩大要素。

在 Java 中,拋出的異??梢苑譃?strong>顯示異常和隱式異常,這種區(qū)分主要來(lái)自拋出異常的主體是什么,顯示和隱式也是站在應(yīng)用程序的視角來(lái)區(qū)分的。

顯示異常的主體是當(dāng)前我們的應(yīng)用程序,它指的是在應(yīng)用程序中使用 “throw” 關(guān)鍵字,主動(dòng)將異常實(shí)例拋出。而隱式異常就不受我們控制, 它觸發(fā)的主體是 Java 虛擬機(jī),指的是 Java 虛擬機(jī)在執(zhí)行過(guò)程中,遇到了無(wú)法繼續(xù)執(zhí)行的異常狀態(tài),續(xù)而將異常拋出。

對(duì)于隱式異常,在觸發(fā)時(shí),需要顯示捕獲(try-catch),或者在方法頭上,用 "throw" 關(guān)鍵字聲明,交由調(diào)用者捕獲處理。

2.2 使用異常捕獲

在我們編寫異常處理代碼的時(shí)候,主要就是使用前面介紹到的 try-catch-finally 這三種代碼塊。

try 代碼塊:包含待監(jiān)控異常的代碼。

catch 代碼塊:緊跟 try 塊之后,可以指定異常類型。允許指定捕獲多種不同的異常,catch 塊用來(lái)捕獲在 try 塊中出發(fā)的某個(gè)指定類型的異常。

finally 代碼塊:緊跟 try 塊或 catch 塊之后,用來(lái)聲明一段必定會(huì)運(yùn)行的代碼。例如用來(lái)清理一些資源。

catch 允許存在多個(gè),用于針對(duì)不同的異常做不同的處理。如果使用 catch 捕獲多種異常,各個(gè) catch 塊是互斥的,和 switch 語(yǔ)句類似,優(yōu)先級(jí)是從上到下,只能選擇其一去處理異常。

既然 try-catch-finally 存在多種情況,并且在發(fā)生異常和不發(fā)生異常時(shí),表現(xiàn)是不一致的,我們就分清楚來(lái)多帶帶分析。

1. try塊中,未發(fā)生異常

不觸發(fā)異常,當(dāng)然是我們樂(lè)于看見(jiàn)的。在這種情況下,如果有 finally 塊,它會(huì)在 try 塊之后運(yùn)行,catch 塊永遠(yuǎn)也不會(huì)被運(yùn)行。

2. try塊中,發(fā)生異常

在發(fā)生異常時(shí),會(huì)首先檢查異常類型,是否存在于我們的 catch 塊中指定的待捕獲異常。如果存在,則這個(gè)異常被捕獲,對(duì)應(yīng)的 catch 塊代碼則開(kāi)始運(yùn)行,finally 塊代碼緊隨其后。

例如:我們只監(jiān)聽(tīng)了空指針(NullPointerException),此時(shí)如果發(fā)生了除數(shù)為 0 的崩潰(ArithmeticException),則是不會(huì)被處理的。

當(dāng)觸發(fā)了我們未捕獲的異常時(shí),finally 代碼依然會(huì)被執(zhí)行,在執(zhí)行完畢后,繼續(xù)將異?!皰伋鋈ァ?。

3. catch 或者 finally 發(fā)生異常

catch 代碼塊和 finally 代碼塊,也是我們編寫的,理論上也是有出錯(cuò)的可能。

那么這兩段代碼發(fā)生異常,會(huì)出現(xiàn)什么情況呢?

當(dāng)在 catch 代碼塊中發(fā)生異常時(shí),此時(shí)的表現(xiàn)取決于 finally 代碼塊中是否存在 return 語(yǔ)句。如果存在,則 finally 代碼塊的代碼執(zhí)行完畢直接返回,否則會(huì)在 finally 代碼塊執(zhí)行完畢后,將 catch 代碼中新產(chǎn)生的異常,向外拋出去。

而在極端情況下,finally 代碼塊發(fā)生了異常,則此時(shí)會(huì)中斷 finally 代碼塊的執(zhí)行,直接將異常向外拋出。

2.3 異常捕獲的返回值

再回頭看看第一個(gè)問(wèn)題,假如我們寫了一個(gè)方法,其中的代碼被 try-catch-finally 包裹住進(jìn)行異常處理,此時(shí)如果我們?cè)诙鄠€(gè)地方都有 return 語(yǔ)句,最終誰(shuí)的會(huì)被執(zhí)行?

如上圖所示,在完整的 try-catch-finally 語(yǔ)句中,finally 都是最后執(zhí)行的,假設(shè) finally 代碼塊中存在 return 語(yǔ)句,則直接返回,它是優(yōu)先級(jí)最高的。

一般我們不建議在 finally 代碼塊中添加 return 語(yǔ)句,因?yàn)檫@會(huì)破壞并阻止異常的拋出,導(dǎo)致不宜排查的崩潰。

2.4 異常的類型

在 Java 中,所有的異常,其實(shí)都是一個(gè)個(gè)異常類,它們都是 Throwable 類或其子類的實(shí)例。

Throwable 有兩大子類,ExceptionError。

Exception:表示程序可能需要捕獲并且處理的異常。

Error:表示當(dāng)觸發(fā) Error 時(shí),它的執(zhí)行狀態(tài)已經(jīng)無(wú)法恢復(fù)了,需要中止線程甚至是中止虛擬機(jī)。這是不應(yīng)該被我們應(yīng)用程序所捕獲的異常。

通常,我們只需要捕獲 Exception 就可以了。但 Exception 中,有一個(gè)特殊的子類 RuntimeException,即運(yùn)行時(shí)錯(cuò)誤,它是在程序運(yùn)行時(shí),動(dòng)態(tài)出現(xiàn)的一些異常。比較常見(jiàn)的就是 NullPointerException、ArrayIndexOutOfBoundsException 等。

Error 和 RuntimeException 都屬于非檢查異常(Unchecked Exception),與之相對(duì)的就是普通 Exception 這種屬于檢查異常(Checked Exception)。

所有檢查異常都需要在程序中,用代碼顯式捕獲,或者在方法中用 throw 關(guān)鍵字顯式標(biāo)注。其實(shí)意思很明顯,要不你自己處理了,要不你拋出去讓別人處理。

這種檢查異常的機(jī)制,是在編譯期間進(jìn)行檢查的,所以如果不按此規(guī)范處理,在編譯器編譯代碼時(shí),就會(huì)拋出異常。

2.5 異常處理的性能問(wèn)題

對(duì)于異常處理的性能問(wèn)題,其實(shí)是一個(gè)很有爭(zhēng)議的問(wèn)題,有人覺(jué)得異常處理是多做了一些工作,肯定對(duì)性能是有影響的。但是也有人覺(jué)得異常處理的影響,和增加一個(gè) if-else 屬于同種量級(jí),對(duì)性能的影響其實(shí)微乎其微,是在可以接受的范圍內(nèi)的。

既然有爭(zhēng)議,最簡(jiǎn)單的辦法是寫個(gè) Demo 驗(yàn)證一下。當(dāng)然,我們這里是需要區(qū)分不同的情況,然后根據(jù)解決對(duì)比的。

一個(gè)最簡(jiǎn)單的 for 循環(huán) 100w 次,在其中做一個(gè) a++ 的自增操作。

A:無(wú)任何 try-catch 語(yǔ)句。

B:將 a++ 包在 try 代碼塊中。

C:在 try 代碼塊中,觸發(fā)一個(gè)異常。

就是一個(gè)簡(jiǎn)單的 for 循環(huán),就不貼代碼了,異常通過(guò) 5/0 這樣的運(yùn)算,觸發(fā)除數(shù)為 0 的 ArithmeticException 異常,并在 JDK 1.8 的環(huán)境下運(yùn)行。

為了避免影響采樣結(jié)果,每個(gè)例子都多帶帶運(yùn)行 10 遍之后,取平均值(單位納秒)。

到這里基本上就可以得出結(jié)論了,在沒(méi)有發(fā)生異常的情況下,try-catch 對(duì)性能的影響微乎其微。但是一旦發(fā)生異常,性能上則是災(zāi)難性的。

因此,我們應(yīng)該盡可能的避免通過(guò)異常來(lái)處理正常的邏輯檢查,這樣可以確保不會(huì)因?yàn)榘l(fā)生異常而導(dǎo)致性能問(wèn)題。

至于為什么發(fā)生異常時(shí),性能差別會(huì)有如此之大,就需要從 Java 虛擬機(jī) JVM 的角度來(lái)分析了,后面會(huì)詳細(xì)分析。

2.6 異常處理無(wú)法覆蓋異步回調(diào)

try-catch-finally 確實(shí)很好用,但是它并不能捕獲,異步回調(diào)中的異常。try 語(yǔ)句里的方法,如果允許在另外一個(gè)線程中,其中拋出的異常,是無(wú)法在調(diào)用者這個(gè)線程中捕獲的。

這一點(diǎn)在使用的過(guò)程中,需要特別注意。

三、JVM 如何處理異常 3.1 JVM 異常處理概述

接下來(lái)我們從 JVM 的角度,分析 JVM 如何處理異常。

當(dāng)異常發(fā)生時(shí),異常實(shí)例的構(gòu)建,是非常消耗性能的。這是由于在構(gòu)造異常實(shí)例時(shí),Java 虛擬機(jī)需要生成該異常的異常棧(stack trace)。

異常棧會(huì)逐一訪問(wèn)當(dāng)前線程的 Java 棧幀,以及各種調(diào)試信息。包括棧幀所指向的方法名,方法所在的類名、文件名以及在代碼中是第幾行觸發(fā)的異常。

這些異常輸出到 Log 中,就是我們熟悉的崩潰日志(崩潰棧)。

3.2 崩潰實(shí)例分析異常處理

當(dāng)把 Java 代碼編譯成字節(jié)碼后,每個(gè)方法都會(huì)附帶一個(gè)異常表,其中記錄了當(dāng)前方法的異常處理。

下面直接舉個(gè)例子,寫一個(gè)最簡(jiǎn)單的 try-catch 類。

使用 javap -c 進(jìn)行反編譯成字節(jié)碼。

可以看到,末尾的 Exceptions Table 就是異常表。異常表中的每一條記錄,都代表了一個(gè)異常處理器。

異常處理器中,標(biāo)記了當(dāng)前異常監(jiān)控的起始、結(jié)束代碼索引,和異常處理器的索引。其中 from 指針和 to 指針標(biāo)識(shí)了該異常處理器所監(jiān)控的代碼范圍,target 指針則指向異常處理器的起始位置,type 則為最后監(jiān)聽(tīng)的異常。

例如上面的例子中,main 函數(shù)中存在異常表,Exception 的異常監(jiān)聽(tīng)代碼范圍分別是 [0,8)(不包括 8),異常處理器的索引為 11。

繼續(xù)分析異常處理流程,還需要區(qū)分是否命中異常。

1. 命中異常

當(dāng)程序發(fā)生異常時(shí),Java 虛擬機(jī)會(huì)從上到下遍歷異常表中所有的記錄。當(dāng)發(fā)現(xiàn)觸發(fā)異常的字節(jié)碼的索引值,在某個(gè)異常表中某個(gè)異常監(jiān)控的范圍內(nèi)。Java 虛擬機(jī)會(huì)判斷所拋出的異常和該條異常監(jiān)聽(tīng)的異常類型,是否匹配。如果能匹配上,Java 虛擬機(jī)會(huì)將控制流轉(zhuǎn)向至該此異常處理器的 target 索引指向的字節(jié)碼,這是命中異常的情況。

2. 未命中異常

而如果遍歷完異常表中所有的異常處理器之后,仍未匹配到異常處理器,那么它會(huì)彈出當(dāng)前方法對(duì)應(yīng)的 Java 棧幀?;氐剿恼{(diào)用者,在其中重復(fù)此過(guò)程。

最壞的情況下,Java 虛擬機(jī)需要遍歷當(dāng)前線程 Java 棧上所有方法的異常表。

3.3 編譯后的 finally 代碼塊

我們寫的代碼,其實(shí)終歸是給人讀的,但是編譯器干的事兒,都不是人事兒。它會(huì)把代碼做一些特殊的處理,只是為了讓自己更好解析和執(zhí)行。

編譯器對(duì) finally 代碼塊,就是這樣處理的。在當(dāng)前版本的 Java 編譯器中,會(huì)將 finally 代碼塊的內(nèi)容,復(fù)制幾份,分別放在所有可能執(zhí)行的代碼路徑的出口中。

寫個(gè) Demo 驗(yàn)證一下,代碼如下。

繼續(xù) javap -c 反編譯成字節(jié)碼。

這個(gè)例子中,為了更清晰的看到 finally 代碼塊,我在其中輸出的一段 Log “run finally”??梢钥吹?,編譯結(jié)果中,包含了三份 finally 代碼塊。

其中,前兩份分別位于 try 代碼塊和 catch 代碼塊的正常執(zhí)行路徑出口。最后一份則作為全局的異常處理器,監(jiān)控 try 代碼塊以及 catch 代碼塊。它將捕獲 try 代碼塊觸發(fā)并且未命中 catch 代碼塊捕獲的異常,以及在 catch 代碼塊觸發(fā)的異常。

而 finally 的代碼,如果出現(xiàn)異常,就不是當(dāng)前方法所能處理的了,會(huì)直接向外拋出。

3.4 異常表中的 any 是什么?

從上圖中可以看到,在異常表中,還存在兩個(gè) any 的信息。

第一個(gè)信息的 from 和 to 的范圍就是 try 代碼塊,等于是對(duì) catch 遺漏異常的一種補(bǔ)充,表示會(huì)處理所有種類的異常。

第二個(gè)信息的 from 和 to 的范圍,仔細(xì)看能看到它其實(shí)是 catch 代碼塊,這也正好印證了我們上面的結(jié)論,catch 代碼塊其實(shí)也被異常處理器監(jiān)控著。

只是如果命中了 any 之后,因?yàn)闆](méi)有對(duì)應(yīng)的異常處理器,會(huì)繼續(xù)向上拋出去,交由該方法的調(diào)用方法處理。

四、總結(jié)

到這里我們就基本上講清楚了 Java 異常處理的所有內(nèi)容。

在日常開(kāi)發(fā)當(dāng)中,應(yīng)該盡量避免使用異常處理的機(jī)制來(lái)處理業(yè)務(wù)邏輯,例如很多代碼中,類型轉(zhuǎn)換就使用 try-catch 來(lái)處理,其實(shí)是很不可取的。

異常捕獲對(duì)應(yīng)用程序的性能確實(shí)有影響,但也是分情況的。

一旦異常被拋出來(lái),方法也就跟著 return 了,捕獲異常棧時(shí)會(huì)導(dǎo)致性能變得很慢,尤其是調(diào)用棧比較深的時(shí)候。

但是從另一個(gè)角度來(lái)說(shuō),異常拋出時(shí),基本上表明程序的錯(cuò)誤。應(yīng)用程序在大多數(shù)情況下,應(yīng)該是在沒(méi)有異常情況的環(huán)境下運(yùn)行的。所以,異常情況應(yīng)該是少數(shù)情況,只要我們不濫用異常處理,基本上不會(huì)影響正常處理的性能問(wèn)題。

本文對(duì)你有幫助嗎?留言、點(diǎn)贊、轉(zhuǎn)發(fā)是最大的支持,謝謝!

公眾號(hào)后臺(tái)回復(fù)成長(zhǎng)『成長(zhǎng)』,將會(huì)得到我準(zhǔn)備的學(xué)習(xí)資料,也能回復(fù)『加群』,一起學(xué)習(xí)進(jìn)步;你還能回復(fù)『提問(wèn)』,向我發(fā)起提問(wèn)。

推薦閱讀:

“寒冬”正是學(xué)習(xí)時(shí)|關(guān)于字符編碼,你需要知道的都在這里 | 分詞,科普及解決方案| 圖解:HTTP 范圍請(qǐng)求 | 小程序?qū)W習(xí)資料 |HTTP 內(nèi)容編碼 | 輔助模式實(shí)戰(zhàn) | 輔助模式玩出花樣 | 小程序 Flex 布局

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/73057.html

相關(guān)文章

  • 16.java異常處理

    摘要:不受檢查異常為編譯器不要求強(qiáng)制處理的異常,檢查異常則是編譯器要求必須處置的異常。潛在的異常處理器是異常發(fā)生時(shí)依次存留在調(diào)用棧中的方法的集合。當(dāng)運(yùn)行時(shí)系統(tǒng)遍歷調(diào)用棧而未找到合適的異常處理器,則運(yùn)行時(shí)系統(tǒng)終止。異常處理涉及到五個(gè)關(guān)鍵字,分別是。 概念 程序運(yùn)行時(shí),發(fā)生的不被期望的事件,它阻止了程序按照程序員的預(yù)期正常執(zhí)行,這就是異常。 異常是程序中的一些錯(cuò)誤,但并不是所有的錯(cuò)誤都是異常,并...

    asce1885 評(píng)論0 收藏0
  • Java異常處理

    摘要:異常也就是指程序運(yùn)行時(shí)發(fā)生錯(cuò)誤,而異常處理就是對(duì)這些錯(cuò)誤進(jìn)行處理和控制。有兩個(gè)重要的子類異常和錯(cuò)誤,二者都是異常處理的重要子類,各自都包含大量子類。需要注意的是,一旦某個(gè)捕獲到匹配的異常類型,將進(jìn)入異常處理代碼。 1,異?,F(xiàn)象 程序錯(cuò)誤分為三種:1,編譯錯(cuò)誤;2,運(yùn)行時(shí)錯(cuò)誤;3,邏輯錯(cuò)誤。 編譯錯(cuò)誤是因?yàn)槌绦驔](méi)有遵循語(yǔ)法規(guī)則,編譯程序能夠自己發(fā)現(xiàn)并且提示我們錯(cuò)誤的原因和位置,這...

    CarlBenjamin 評(píng)論0 收藏0
  • Java異常處理

    摘要:可以被異常處理機(jī)制使用,是異常處理的核心。非檢測(cè)異常,在編譯時(shí),不會(huì)提示和發(fā)現(xiàn)異常的存在,不強(qiáng)制要求程序員處理這樣的異常??傮w來(lái)說(shuō),語(yǔ)言的異常處理流程,從程序中獲取異常信息。處理運(yùn)行時(shí)異常,采用邏輯合理規(guī)避同時(shí)輔助處理。 目錄 什么是Java異常? 當(dāng)一個(gè)Exception在程序中發(fā)生的時(shí)候,JVM是怎么做的呢? 當(dāng)我們編寫程序的時(shí)候如何對(duì)待可能出現(xiàn)的異常呢? 正文 1. 什么是J...

    Fourierr 評(píng)論0 收藏0
  • Java 異常處理

    摘要:下面是異常處理機(jī)制的語(yǔ)法結(jié)構(gòu)業(yè)務(wù)實(shí)現(xiàn)代碼輸入不合法如果執(zhí)行塊里業(yè)務(wù)邏輯代碼時(shí)出現(xiàn)異常,系統(tǒng)自動(dòng)生成一個(gè)異常對(duì)象,該對(duì)象被提交給運(yùn)行時(shí)環(huán)境,這個(gè)過(guò)程被稱為拋出異常。 Java的異常機(jī)制主要依賴于try、catch、finally、throw和throws五個(gè)關(guān)鍵字, try關(guān)鍵字后緊跟一個(gè)花括號(hào)括起來(lái)的代碼塊(花括號(hào)不可省略),簡(jiǎn)稱try塊,它里面放置可能引發(fā)異常的代碼 catch后對(duì)...

    senntyou 評(píng)論0 收藏0
  • java異常處理機(jī)制的理解

    摘要:根據(jù)異常對(duì)象判斷是否存在異常處理。否則,范圍小的異常會(huì)因異常處理完成而無(wú)法處理。異常處理中使用作為異常的統(tǒng)一出口。 參考《第一行代碼java》《java程序設(shè)計(jì)教程》java中程序的錯(cuò)誤有語(yǔ)法錯(cuò)誤、語(yǔ)義錯(cuò)誤。如果是語(yǔ)法性錯(cuò)誤,在編譯時(shí)就可以檢查出來(lái)并解決。語(yǔ)義錯(cuò)誤是在程序運(yùn)行時(shí)出現(xiàn)的,在編譯時(shí)沒(méi)有錯(cuò)誤,但在運(yùn)行時(shí)可能會(huì)出現(xiàn)錯(cuò)誤導(dǎo)致程序退出,這些錯(cuò)誤稱為異常。在沒(méi)有異常處理的情況下,也即...

    khs1994 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<