国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

聊聊Java的異常機制及實現

Towers / 2664人閱讀

摘要:是那些可能在虛擬機正常運行期間拋出的異常的超類。運行時異常定義及其子類都被稱為運行時異常。對于語言中的關鍵字和,虛擬機中并沒有特殊的字節碼指令去支持它們,都是通過編譯器生成字節碼片段以及不同的異常處理器來實現。

前言

在一些傳統的編程語言,如C語言中,并沒有專門處理異常的機制,程序員通常用方法的特定返回值來表示異常情況,并且程序的正常流程和異常流程都采用同樣的流程控制語句。
Java語言按照面向對象的思想來處理異常,使得程序具有更好的可維護性。Java異常處理機制具有以下優點:

把各種不同類型的異常情況進行分類,用Java類來表示異常情況,這種類被稱為異常類。把異常情況表示成異常類,可以充分發揮類的可擴展和可重用的優勢。

異常流程的代碼和正常流程的代碼分離,提高了程序的可讀性,簡化程序的結構。

可以靈活的處理異常,如果當前方法有能力處理異常,就捕獲并處理它,否則只需要拋出異常,由方法調用。

Java異常基礎

關于異常的使用我就不再多說了,在這里還是先提幾個問題:

catch多個異常的時候,按什么規則選擇呢

throws異常是否是函數簽名的一部分呢

覆蓋父類的帶throws的函數是否也需要加throws呢

同時實現多個接口中同名拋出異常的函數最后拋出異常的集合是什么呢

接下來我們回答其中的部分問題,先看一個例子

可以看到Java是按照catch聲明的順序來捕獲異常的,且編譯器不允許將父類異常聲明在子類之前。

throws異常顯然不是函數的一部分,因為兩個throws不同的同名同參數的函數不允許重載。

從上圖我們可以看出覆蓋對拋出異常的聲明并沒有要求。

上圖可以看出編譯器對接口的方法實現也并無什么要求,重點在于try-catch塊的檢查,你不能catch一個你在throw塊里不可能拋出的檢查類型異常,而這種判斷是通過你調用方法聲明的拋出異常,即使你在方法實現里不可能拋出該異常,你加在throws里,一樣可以蒙騙編譯器。對于方法聲明的拋出異常,只有一個條件需要滿足,那就是你的實現中可能拋出的檢查類型異常要么處理要么聲明拋出,不需要考慮繼承和實現關系給throws帶來的影響,這是參考文章中的一點小錯誤,特此更正。

Java異常類的架構

Throwable

Throwable是 Java 語言中所有錯誤或異常的超類。

Throwable包含兩個子類: Error 和 Exception。它們通常用于指示發生了異常情況。

Throwable包含了其線程創建時線程執行堆棧的快照,它提供了printStackTrace()等接口用于獲取堆棧跟蹤數據等信息。

Exception

Exception及其子類是 Throwable 的一種形式,它指出了合理的應用程序想要捕獲的條件。

RuntimeException

RuntimeException是那些可能在 Java 虛擬機正常運行期間拋出的異常的超類。

編譯器不會檢查RuntimeException異常。例如,除數為零時,拋出ArithmeticException異常。RuntimeException是ArithmeticException的超類。當代碼發生除數為零的情況時,倘若既"沒有通過throws聲明拋出ArithmeticException異常",也"沒有通過try...catch...處理該異常",也能通過編譯。這就是我們所說的"編譯器不會檢查RuntimeException異常"!

如果代碼會產生RuntimeException異常,則需要通過修改代碼進行避免。例如,若會發生除數為零的情況,則需要通過代碼避免該情況的發生!

Error

和Exception一樣,Error也是Throwable的子類。它用于指示合理的應用程序不應該試圖捕獲的嚴重問題,大多數這樣的錯誤都是異常條件。

和RuntimeException一樣,編譯器也不會檢查Error。

Java將可拋出(Throwable)的結構分為三種類型:被檢查的異常(Checked Exception),運行時異常(RuntimeException)和錯誤(Error)。

(01) 運行時異常

定義: RuntimeException及其子類都被稱為運行時異常。

特點: Java編譯器不會檢查它。也就是說,當程序中可能出現這類異常時,倘若既"沒有通過throws聲明拋出它",也"沒有用try-catch語句捕獲它",還是會編譯通過。例如,除數為零時產生的ArithmeticException異常,數組越界時產生的IndexOutOfBoundsException異常,fail-fail機制產生的ConcurrentModificationException異常等,都屬于運行時異常。

雖然Java編譯器不會檢查運行時異常,但是我們也可以通過throws進行聲明拋出,也可以通過try-catch對它進行捕獲處理。

如果產生運行時異常,則需要通過修改代碼來進行避免。例如,若會發生除數為零的情況,則需要通過代碼避免該情況的發生!

(02) 被檢查的異常

定義: Exception類本身,以及Exception的子類中除了"運行時異常"之外的其它子類都屬于被檢查異常。

特點: Java編譯器會檢查它。此類異常,要么通過throws進行聲明拋出,要么通過try-catch進行捕獲處理,否則不能通過編譯。例如,CloneNotSupportedException就屬于被檢查異常。當通過clone()接口去克隆一個對象,而該對象對應的類沒有實現Cloneable接口,就會拋出CloneNotSupportedException異常。

被檢查異常通常都是可以恢復的。

(03) 錯誤

定義: Error類及其子類。

特點: 和運行時異常一樣,編譯器也不會對錯誤進行檢查。

當資源不足、約束失敗、或是其它程序無法繼續運行的條件發生時,就產生錯誤。程序本身無法修復這些錯誤的。例如,VirtualMachineError就屬于錯誤。

按照Java慣例,我們是不應該是實現任何新的Error子類的!

Java異常的實現原理 異常的捕獲原理

首先介紹下java的異常表(Exception table),異常表是JVM處理異常的關鍵點,在java類中的每個方法中,會為所有的try-catch語句,生成一張異常表,存放在字節碼的最后,該表記錄了該方法內每個異常發生的起止指令和處理指令。

接下來看一個例子:

public void catchException() {  
    long l = System.nanoTime();  
    for (int i = 0; i < testTimes; i++) { 
        try {  
            throw new Exception();  
        } catch (Exception e) { 
            //nothing to do
        }  
    }
    System.out.println("拋出并捕獲異常:" + (System.nanoTime() - l));  
}

字節碼如下

面請結合java代碼和生成的字節碼來看下面的指令分析:
0-4號: 執行try前面的語句
5號: 執行try語句前保存現場
6號: 執行try語句后跳轉指令行,圖中表示跳轉到22
9-17號: try-catch代碼生成指令,結合紅色框圖異常表,表示9-17號指令若有Exception異常拋出就執行17行指令.
16號: athrow 表示拋出異常
17號: astore 表示jvm將該異常實例存儲到局部變量表中方便一旦出方法棧調用方可以找到
22號: 恢復try語句執行前保存的現場
對比指令分析,再結合使用try-catch代碼分析:

若try沒有拋出異常,則繼續執行完try語句,跳過catch語句,此時就是從指令6跳轉到指令22.

若try語句拋出異常則執行指令17,將異常保存起來,若異常被方法拋出,調用方拿到異常可用于異常層次索引。

通過以上的分析,可以知道JVM是怎么捕獲并處理異常,其實就是使用goto指令來做上下文切換。

異常的處理機制

上面大致介紹了異常是如何產生并捕獲的,接下來我們詳細講講athrow指令拋出異常后的故事,也就是如何處理異常的問題。

athrow指令,這個指令運作過程大致是首先檢查操作棧頂,這時棧頂必須存在一個reference類型的值,并且是java.lang.Throwable的子類(虛擬機規范中要求如果遇到null則當作NPE異常使用),然后暫時先把這個引用出棧,接著搜索本方法的異常表,找一下本方法中是否有能處理這個異常的handler,如果能找到合適的handler就會重新初始化PC寄存器指針指向此異常handler的第一個指令的偏移地址。接著把當前棧幀的操作棧清空,再把剛剛出棧的引用重新入棧。如果在當前方法中很悲劇的找不到handler,那只好把當前方法的棧幀出棧(這個棧是VM棧,不要和前面的操作棧搞混了,棧幀出棧就意味著當前方法退出),這個方法的調用者的棧幀就自然在這條線程VM棧的棧頂了,然后再對這個新的當前方法再做一次剛才做過的異常handler搜索,如果還是找不到,繼續把這個棧幀踢掉,這樣一直到找,要么找到一個能使用的handler,轉到這個handler的第一條指令開始繼續執行,要么把VM棧的棧幀拋光了都沒有找到期望的handler,這樣的話這條線程就只好被迫終止、退出了。

對于Java語言中的關鍵字catch和finally,虛擬機中并沒有特殊的字節碼指令去支持它們,都是通過編譯器生成字節碼片段以及不同的異常處理器來實現。

我們總結一下athrow指令中虛擬機可能做的事情:

檢查棧頂異常對象類型(只檢查是不是null,是否referance類型,是否Throwable的子類一般在類驗證階段的數據流分析中做,或者索性不做靠編譯器保證了,編譯時寫到Code屬性的StackMapTable中,在加載時僅做類型驗證)

把異常對象的引用出棧

搜索異常表,找到匹配的異常handler

重置PC寄存器狀態

清理操作棧

把異常對象的引用入棧

把異常方法的棧幀逐個出棧(這里的棧是VM棧)

殘忍地終止掉當前線程。

異常到底慢不慢

這里直接給出一些結論吧:

新建一個異常對象比新建一個普通對象在耗時上多一個數量級,拋出并捕獲異常的耗時比新建一個異常在耗時上也要多一個數量級。創建一個異常對象卻是要比一個普通對象耗時多,捕獲一個異常耗時更甚。捕獲的過程我們上面已經簡要介紹了,為什么新建一個異常對象這么耗時?且看源碼:

在java中,所有的異常都繼承自Throwable類,Throwable的構造函數

public Throwable() {
    ...
    fillInStackTrace();
    ...
}

有個nativ方法public synchronized native Throwable fillInStackTrace();這個方法會存入當前線程的堆棧信息。也就是說每次創建一個異常實例都會把堆棧信息存一遍。這就是時間開銷的主要來源了。

這個時候我們可以下一個結論:新建異常對象比創建一個普通對象是要更加的耗時。

能避開創建異常的這個耗時嗎?答案是可以的,如果在程序中我們不關心異常拋出的異常占信息,我們可以自己定義一個異常繼承自已有的異常類型,并寫一個方法覆蓋掉fillInStackTrace方法就行了。

參考文章

Java異常簡介及其架構

關于異常處理的幾條建議

關于異常的幾個謎題(必看)

異常分析初探

透過JVM看Exception本質

三言兩語:JVM 字節碼執行實例分析

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/67207.html

相關文章

  • Java知識點匯總

    摘要:由于類型擦除機制的存在,泛型類中的類型參數等信息,在運行時刻是不存在的。對此,對類文件的格式做了修訂,添加了屬性,用來包含不在類型系統中的類型信息。在運行時刻,會讀取屬性的內容并提供給反射來使用。 OOP 對象的創建和拷貝 對象的初始化 多態的實現 內部類、匿名類、靜態類 對象內存模型 上面內容均請參考以下文章: 談談Java的面向對象 運行時 異常 聊聊Java的異常機制及實現 ...

    Chao 評論0 收藏0
  • 聊聊Java泛型實現

    摘要:靜態變量是被泛型類的所有實例所共享的。所以引用能完成泛型類型的檢查。對于這個類型系統,有如下的一些規則相同類型參數的泛型類的關系取決于泛型類自身的繼承體系結構。事實上,泛型類擴展都不合法。 前言 和C++以模板來實現靜多態不同,Java基于運行時支持選擇了泛型,兩者的實現原理大相庭徑。C++可以支持基本類型作為模板參數,Java卻只能接受類作為泛型參數;Java可以在泛型類的方法中取得...

    lewif 評論0 收藏0
  • Java面試通關要點匯總集

    摘要:本文會以引出問題為主,后面有時間的話,筆者陸續會抽些重要的知識點進行詳細的剖析與解答。敬請關注服務端思維微信公眾號,獲取最新文章。 原文地址:梁桂釗的博客博客地址:http://blog.720ui.com 這里,筆者結合自己過往的面試經驗,整理了一些核心的知識清單,幫助讀者更好地回顧與復習 Java 服務端核心技術。本文會以引出問題為主,后面有時間的話,筆者陸續會抽些重要的知識點進...

    gougoujiang 評論0 收藏0
  • 聊聊Dubbo - Dubbo可擴展機制實戰

    摘要:今天我想聊聊的另一個很棒的特性就是它的可擴展性。的擴展機制在的官網上,描述自己是一個高性能的框架。接下來的章節中我們會慢慢揭開擴展機制的神秘面紗。擴展擴展點的實現類。的定義在配置文件中可以看到文件中定義了個的擴展實現。 摘要: 在Dubbo的官網上,Dubbo描述自己是一個高性能的RPC框架。今天我想聊聊Dubbo的另一個很棒的特性, 就是它的可擴展性。 Dubbo的擴展機制 在Dub...

    techstay 評論0 收藏0
  • 聊聊Dubbo - Dubbo可擴展機制源碼解析

    摘要:什么是類那什么樣類的才是擴展機制中的類呢類是一個有復制構造函數的類,也是典型的裝飾者模式。代碼如下有一個參數是的復制構造函數有一個構造函數,參數是擴展點,所以它是一個擴展機制中的類。 摘要:?在Dubbo可擴展機制實戰中,我們了解了Dubbo擴展機制的一些概念,初探了Dubbo中LoadBalance的實現,并自己實現了一個LoadBalance。是不是覺得Dubbo的擴展機制很不錯呀...

    lmxdawn 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<