1. 程式人生 > >異常(編譯時異常和執行時異常)

異常(編譯時異常和執行時異常)

 1. 引子

       try…catch…finally恐怕是大家再熟悉不過的語句了,而且感覺用起來也是很簡單,邏輯上似乎也是很容易理解。不過,我親自體驗的“教訓”告訴我,這個東西可不是想象中的那麼簡單、聽話。不信?那你看看下面的程式碼,“猜猜”它執行後的結果會是什麼?不要往後看答案、也不許執行程式碼看真正答案哦。如果你的答案是正確,那麼這篇文章你就不用浪費時間看啦。

[java] view plain copy  print?在CODE上檢視程式碼片派生到我的程式碼片
  1. package Test;  
  2. publicclass TestException {  
  3.     public
     TestException() {  
  4.     }  
  5.     boolean testEx() throws Exception {  
  6.         boolean ret = true;  
  7.         try {  
  8.             ret = testEx1();  
  9.         } catch (Exception e) {  
  10.             System.out.println("testEx, catch exception");  
  11.             ret = false;  
  12.             throw e;  
  13.         } finally {  
  14.             System.out.println("testEx, finally; return value=" + ret);  
  15.             return ret;  
  16.         }  
  17.     }  
  18.     boolean testEx1() throws Exception {  
  19.         boolean ret = true;  
  20.         try {  
  21.             ret = testEx2();  
  22.             if (!ret) {  
  23.                 return
    false;  
  24.             }  
  25.             System.out.println("testEx1, at the end of try");  
  26.             return ret;  
  27.         } catch (Exception e) {  
  28.             System.out.println("testEx1, catch exception");  
  29.             ret = false;  
  30.             throw e;  
  31.         } finally {  
  32.             System.out.println("testEx1, finally; return value=" + ret);  
  33.             return ret;  
  34.         }  
  35.     }  
  36.     boolean testEx2() throws Exception {  
  37.         boolean ret = true;  
  38.         try {  
  39.             int b = 12;  
  40.             int c;  
  41.             for (int i = 2; i >= -2; i--) {  
  42.                 c = b / i;  
  43.                 System.out.println("i=" + i);  
  44.             }  
  45.             returntrue;  
  46.         } catch (Exception e) {  
  47.             System.out.println("testEx2, catch exception");  
  48.             ret = false;  
  49.             throw e;  
  50.         } finally {  
  51.             System.out.println("testEx2, finally; return value=" + ret);  
  52.             return ret;  
  53.         }  
  54.     }  
  55.     publicstaticvoid main(String[] args) {  
  56.         TestException testException1 = new TestException();  
  57.         try {  
  58.             testException1.testEx();  
  59.         } catch (Exception e) {  
  60.             e.printStackTrace();  
  61.         }  
  62.     }  
  63. }  

你的答案是什麼?是下面的答案嗎?

i=2
i=1
testEx2, catch exception
testEx2, finally; return value=false
testEx1, catch exception
testEx1, finally; return value=false
testEx, catch exception
testEx, finally; return value=false

如果你的答案真的如上面所說,那麼你錯啦。^_^,那就建議你仔細看一看這篇文章或者拿上面的程式碼按各種不同的情況修改、執行、測試,你會發現有很多事情不是原來想象中的那麼簡單的。現在公佈正確答案:

i=2
i=1
testEx2, catch exception
testEx2, finally; return value=false
testEx1, finally; return value=false
testEx, finally; return value=false

注意說明:

finally語句塊不應該出現 應該出現return。上面的return ret最好是其他語句來處理相關邏輯。

 2.Java異常

異常指不期而至的各種狀況,如:檔案找不到、網路連線失敗、非法引數等。異常是一個事件,它發生在程式執行期間,干擾了正常的指令流程。Java通 過API中Throwable類的眾多子類描述各種不同的異常。因而,Java異常都是物件,是Throwable子類的例項,描述了出現在一段編碼中的 錯誤條件。當條件生成時,錯誤將引發異常。

      Java異常類層次結構圖:


        

                                                                    圖1 Java異常類層次結構圖

        在 Java 中,所有的異常都有一個共同的祖先 Throwable(可丟擲)。Throwable 指定程式碼中可用異常傳播機制通過 Java 應用程式傳輸的任何問題的共性。
       Throwable: 有兩個重要的子類:Exception(異常)和 Error(錯誤),二者都是 Java 異常處理的重要子類,各自都包含大量子類。

       Error(錯誤):是程式無法處理的錯誤,表示執行應用程式中較嚴重問題。大多數錯誤與程式碼編寫者執行的操作無關,而表示程式碼執行時 JVM(Java 虛擬機器)出現的問題。例如,Java虛擬機器執行錯誤(Virtual MachineError),當 JVM 不再有繼續執行操作所需的記憶體資源時,將出現 OutOfMemoryError。這些異常發生時,Java虛擬機器(JVM)一般會選擇執行緒終止。

。這些錯誤表示故障發生於虛擬機器自身、或者發生在虛擬機器試圖執行應用時,如Java虛擬機器執行錯誤(Virtual MachineError)、類定義錯誤(NoClassDefFoundError)等。這些錯誤是不可查的,因為它們在應用程式的控制和處理能力之 外,而且絕大多數是程式執行時不允許出現的狀況。對於設計合理的應用程式來說,即使確實發生了錯誤,本質上也不應該試圖去處理它所引起的異常狀況。在 Java中,錯誤通過Error的子類描述。

       Exception(異常):是程式本身可以處理的異常。

       Exception 類有一個重要的子類 RuntimeException。RuntimeException 類及其子類表示“JVM 常用操作”引發的錯誤。例如,若試圖使用空值物件引用、除數為零或陣列越界,則分別引發執行時異常(NullPointerException、ArithmeticException)和 ArrayIndexOutOfBoundException。

   注意:異常和錯誤的區別:異常能被程式本身可以處理,錯誤是無法處理。

通常,Java的異常(包括Exception和Error)分為可查的異常(checked exceptions)和不可查的異常(unchecked exceptions)
可查異常(編譯器要求必須處置的異常):正確的程式在執行中,很容易出現的、情理可容的異常狀況可查異常雖然是異常狀況,但在一定程度上它的發生是可以預計的,而且一旦發生這種異常狀況,就必須採取某種方式進行處理。

      除了RuntimeException及其子類以外,其他的Exception類及其子類都屬於可查異常。這種異常的特點是Java編譯器會檢查它,也就是說,當程式中可能出現這類異常,要麼用try-catch語句捕獲它,要麼用throws子句宣告丟擲它,否則編譯不會通過。

不可查異常(編譯器不要求強制處置的異常):包括執行時異常(RuntimeException與其子類)和錯誤(Error)。

     Exception 這種異常分兩大類執行時異常和非執行時異常(編譯異常)。程式中應當儘可能去處理這些異常。

       執行時異常:都是RuntimeException類及其子類異常,如NullPointerException(空指標異常)、IndexOutOfBoundsException(下標越界異常)等,這些異常是不檢查異常,程式中可以選擇捕獲處理,也可以不處理。這些異常一般是由程式邏輯錯誤引起的,程式應該從邏輯角度儘可能避免這類異常的發生。

      執行時異常的特點是Java編譯器不會檢查它,也就是說,當程式中可能出現這類異常,即使沒有用try-catch語句捕獲它,也沒有用throws子句宣告丟擲它,也會編譯通過。
       非執行時異常 (編譯異常):是RuntimeException以外的異常,型別上都屬於Exception類及其子類。從程式語法角度講是必須進行處理的異常,如果不處理,程式就不能編譯通過。如IOException、SQLException等以及使用者自定義的Exception異常,一般情況下不自定義檢查異常

 4.處理異常機制

在 Java 應用程式中,異常處理機制為:丟擲異常,捕捉異常。

        丟擲異常當一個方法出現錯誤引發異常時,方法建立異常物件並交付執行時系統,異常物件中包含了異常型別和異常出現時的程式狀態等異常資訊。執行時系統負責尋找處置異常的程式碼並執行。

        捕獲異常:在方法丟擲異常之後,執行時系統將轉為尋找合適的異常處理器(exception handler)。潛在的異常處理器是異常發生時依次存留在呼叫棧中的方法的集合。當異常處理器所能處理的異常型別與方法丟擲的異常型別相符時,即為合適 的異常處理器。執行時系統從發生異常的方法開始,依次回查呼叫棧中的方法,直至找到含有合適異常處理器的方法並執行。當執行時系統遍歷呼叫棧而未找到合適 的異常處理器,則執行時系統終止。同時,意味著Java程式的終止。

        對於執行時異常、錯誤或可查異常,Java技術所要求的異常處理方式有所不同。

        由於執行時異常的不可查性,為了更合理、更容易地實現應用程式,Java規定,執行時異常將由Java執行時系統自動丟擲,允許應用程式忽略執行時異常。

       對於方法執行中可能出現的Error,當執行方法不欲捕捉時,Java允許該方法不做任何丟擲宣告。因為,大多數Error異常屬於永遠不能被允許發生的狀況,也屬於合理的應用程式不該捕捉的異常。

       對於所有的可查異常,Java規定:一個方法必須捕捉,或者宣告丟擲方法之外。也就是說,當一個方法選擇不捕捉可查異常時,它必須宣告將丟擲異常。

        能夠捕捉異常的方法,需要提供相符型別的異常處理器。所捕捉的異常,可能是由於自身語句所引發並丟擲的異常,也可能是由某個呼叫的方法或者Java執行時 系統等丟擲的異常。也就是說,一個方法所能捕捉的異常,一定是Java程式碼在某處所丟擲的異常簡單地說,異常總是先被丟擲,後被捕捉的。

         任何Java程式碼都可以丟擲異常,如:自己編寫的程式碼、來自Java開發環境包中程式碼,或者Java執行時系統。無論是誰,都可以通過Java的throw語句丟擲異常。

        從方法中丟擲的任何異常都必須使用throws子句。

        捕捉異常通過try-catch語句或者try-catch-finally語句實現。

         總體來說,Java規定:對於可查異常必須捕捉、或者宣告丟擲。允許忽略不可查的RuntimeException和Error。

4.1 捕獲異常:try、catch 和 finally

1.try-catch語句

在Java中,異常通過try-catch語句捕獲。其一般語法形式為:

[java] view plain copy  print?在CODE上檢視程式碼片派生到我的程式碼片
  1. try {  
  2.     // 可能會發生異常的程式程式碼
  3. catch (Type1 id1){  
  4.     // 捕獲並處置try丟擲的異常型別Type1
  5. }  
  6. catch (Type2 id2){  
  7.      //捕獲並處置try丟擲的異常型別Type2
  8. }  

 關鍵詞try後的一對大括號將一塊可能發生異常的程式碼包起來,稱為監控區域。Java方法在執行過程中出現異常,則建立異常物件。將異常丟擲監控區域之 外,由Java執行時系統試圖尋找匹配的catch子句以捕獲異常。若有匹配的catch子句,則執行其異常處理程式碼,try-catch語句結束。

       匹配的原則是:如果丟擲的異常物件屬於catch子句的異常類,或者屬於該異常類的子類,則認為生成的異常物件與catch塊捕獲的異常型別相匹配。

例1  捕捉throw語句丟擲的“除數為0”異常。

[java] view plain copy  print?

相關推薦

springcloud系列—Hystrix—第3章-3: Hystrix 服務降級fallback異常處理,Hystrix依賴隔離命令名稱-分組執行緒池、請求快取與清除快取、斷路器

資料參考:《Spring Cloud 微服務實戰》 目錄 服務降級 在HystrixCommand中可以通過過載getFallback()方法來實現服務降級邏輯。 在 HystrixObservableCommand 實現得 Hystrix 命令中,我們可以通過過載 resumenW

編譯型別執行型別---關於型別轉換的原理解析

轉:https://www.cnblogs.com/aademeng/articles/6190143.html 先上程式碼: Class A: package testone; public class A { String a = "This is Cl

Java基礎 | 編譯型別執行型別

一、前言 最近在做筆試題的時候,才看到有這麼一個知識點,查了好幾篇部落格,在這裡記錄一下 二、是什麼 Java引用變數有兩個型別,一個是編譯時型別,還有一個是執行時型別。 編譯時型別是由宣告該變數時使用的型別所決定,執行時型別是由該變數指向的物件型別決定 如果

JAVA的編譯錯誤執行錯誤

1. 要區分編譯時錯誤和執行時錯誤,就應該先明白什麼是編譯?什麼是執行? 首先,先看一下這張圖: 編譯期就是將我們寫的java原始碼交給編譯器執行的過程,起翻譯的作用,該過程主要對java原始碼的語法進行檢查,如果沒有語法錯誤,就將原始碼編譯成位元組

異常編譯異常執行異常

 1. 引子        try…catch…finally恐怕是大家再熟悉不過的語句了,而且感覺用起來也是很簡單,邏輯上似乎也是很容易理解。不過,我親自體驗的“教訓”告訴我,這個東西可不是想象中的那麼簡單、聽話。不信?那你看看下面的程式碼,“猜猜”它

java編譯異常執行異常

一 什麼是編譯時異常,什麼是執行時異常 執行時異常可以通過改變程式避免這種情況發生,比如,除數為0異常,可以先判斷除數是否是0,如果是0,則結束此程式。從繼承上來看,只要是繼承RunTimeException類的,都是執行時異常,其它為編譯時異常。 二編譯時異常和執

編譯異常執行異常的區別

最簡單的說法: javac出來的異常就是編譯時異常,就是說把原始碼編譯成位元組碼(class)檔案時報的異常,一般如果用Eclispe,你敲完程式碼儲存的時候就是編譯的時候。java出來的異常就是執行時異常 Java異常可分為3種:   (1)編譯時異常:Java.

異常編譯異常執行異常&throwthrows區別try-catch的應用

1、編譯時被檢測異常:只要是Exception和其子類都是,除了特殊子類:RuntimeException體系;這種問題一旦出現,希望在編譯時就進行檢測,讓這種問題有對應的處理方式。 2、編譯時不檢測的異常(執行時異常):就是Exception中的Runtim

java異常處理 Exception、error、執行異常一般異常有何異同

一、開場白 對於程式執行過程中的可能出現異常情況,java語言使用一種稱為異常處理的錯誤捕捉機制進行處理。相信大家對 try { }catch( ){} finally{} 這種結構非常熟悉,使用頻率極高。既然經常使用它,而且也是面試常問知識點,我們就有必要去

spring boot啟動異常Unable to start embedded Tomcat servlet container

org.springframework.boot.context.embedded.EmbeddedServletContainerException: **Unable to start embedded Tomcat servlet container** at org.sprin

執行編譯超程式設計—執行超程式設計

執行時和編譯時超程式設計 第一部分 Groovy語言支援兩種風格的超程式設計:執行時超程式設計和編譯時超程式設計。第一種超程式設計支援在程式執行時修改類模型和程式行為,而第二種發生在編譯時。兩種超程式設計有各自的優缺點,在這一章節我們將詳細討論。 注:譯者也是第一次接觸Groovy,由於

第五十八條 對可恢復的情況使用受檢異常,對程式設計錯誤使用執行異常

對於異常,我們知道,基類為 Throwable,它有兩個子類,Exception 和 Error,Exception 通常被稱為異常, Error被稱為錯誤。Exception異常又分為兩種,一種是受檢異常(checkedexception) ,另一種是執行時異常(runtime exceptio

linux下編譯、連線及執行環境變數設定boost庫為例

以boost庫的存放目錄/usr/boost為例, 開啟/etc/profile, 追加以下內容(前兩行為編譯時路徑): export CPLUS_INCLUDE_PATH=/usr/boost/include:$CPLUS_INCLUDE_PATH export LIB

Java 編譯多型執行多型

        根據何時確定執行多型方法中的哪一個,多型分為兩種情況:編譯時多型和執行時多型。如果在編譯時能夠確定執行多型方法 中的哪一個,稱為編譯時多型,否則稱為執行時多型。 一、編譯時多型 方法

GCC編譯、連結、執行庫查詢順序最真實可信

參考了不少資料,其中最靠譜是這個:http://www.mingw.org/wiki/librarypathhowto和http://www.kaizou.org/2015/01/linux-libraries/經過線上實際驗證,GCC編譯、連結、執行時庫查詢順序如下,這個順

Effective Java之對可恢復的情況使用受檢異常,對程式設計錯誤使用執行異常(五十八)

java將所有的錯誤封裝為一個物件,其根本父類為Throwable, Throwable有兩個子類:Error和Exception。 異常分成三種結構 1.錯誤:Error是Throwable 的子類,用於指示合理的應用程式不應該試圖捕獲的嚴重問題。

利用Objective-C的反射機制執行特性實現類靜態方法的動態訪問

如題,灑家今天在搭建蘋果手機APP開發框架中遇到一個坑爹問題,折騰了半天,總算研究出來了,特記錄如下: 1、先說具體需求,本人實現了一個自定義檢視控制元件,通過KVC特性先從plist配置檔案中讀取資料,轉換成模型物件,然後根據模型物件動態建立檢視物件,這時就需要用到Obj

利用Objective-C的反射機制執行特性實現類靜態方法的動態訪問

繼上次的研究成果繼續深入研究,灑家又完善了下在執行時動態呼叫所有OC類方法的公用方法: typedef void*(*ObjcMsgSend)(id, SEL, ...); - (void *)invoke:(id)inst method:(NSString *)nam

C/C++程式編譯執行記憶體區域分配

         3.heap區,存放內容和上文同。值得說明的是:stack區起始地址是在高地址,即是從高地址向低地址延伸。而heap區起始地址是在低地址,即是從低地址向高地址延伸。總結:stack起始地址固定在高地址,heap起始地址固定在低地址,然後兩個區都向中間延伸。直到stack區和heap區的結束

對可恢復的情況使用受檢異常,對程式設計錯誤使用執行異常

    Java程式設計語言提供了三種可丟擲結構:受檢的異常(checked exception)、執行時異常(run-time exception)和錯誤(error)。關於什麼時候適合使用哪種可丟擲結構,程式設計師中間存在一些困惑。雖然這項決定並不總是那麼清晰,但還是有些