㈠ java中exception的處理
有catch 就說明處理了 就算catch裡面什麼也不寫 那也叫空處理 錯誤不會影響程序繼續運行
你說那個 叫 throw 是往外拋異常 如果外層沒有處理 即外層沒有 try catch 繼續向外跑
直到被處理 如果一直沒處理 就會報錯
㈡ java異常處理詳解!!
異常處理是程序設計中一個非常重要的方面,也是程序設計的一大難點,從C開始,你也許已經知道如何用if...else...來控制異常了,也許是自發的,然而這種控制異常痛苦,同一個異常或者錯誤如果多個地方出現,那麼你每個地方都要做相同處理,感覺相當的麻煩! Java語言在設計的當初就考慮到這些問題,提出異常處理的框架的方案,所有的異常都可以用一個類型來表示,不同類型的異常對應不同的子類異常(這里的異常包括錯誤概念),定義異常處理的規范,在1.4版本以後增加了異常鏈機制,從而便於跟蹤異常!這是Java語言設計者的高明之處,也是Java語言中的一個難點,下面是我對Java異常知識的一個總結,也算是資源回收一下。
一、Java異常的基礎知識
異常是程序中的一些錯誤,但並不是所有的錯誤都是異常,並且錯誤有時候是可以避免的。比如說,你的代碼少了一個分號,那麼運行出來結果是提示是錯誤java.lang.Error;如果你用System.out.println(11/0),那麼你是因為你用0做了除數,會拋出java.lang.ArithmeticException的異常。 有些異常需要做處理,有些則不需要捕獲處理,後面會詳細講到。 天有不測風雲,人有旦夕禍福,Java的程序代碼也如此。在編程過程中,首先應當盡可能去避免錯誤和異常發生,對於不可避免、不可預測的情況則在考慮異常發生時如何處理。 Java中的異常用對象來表示。Java對異常的處理是按異常分類處理的,不同異常有不同的分類,每種異常都對應一個類型(class),每個異常都對應一個異常(類的)對象。 異常類從哪裡來?有兩個來源,一是Java語言本身定義的一些基本異常類型,二是用戶通過繼承Exception類或者其子類自己定義的異常。Exception 類及其子類是 Throwable 的一種形式,它指出了合理的應用程序想要捕獲的條件。 異常的對象從哪裡來呢?有兩個來源,一是Java運行時環境自動拋出系統生成的異常,而不管你是否願意捕獲和處理,它總要被拋出!比如除數為0的異常。二是程序員自己拋出的異常,這個異常可以是程序員自己定義的,也可以是Java語言中定義的,用throw 關鍵字拋出異常,這種異常常用來向調用者匯報異常的一些信息。 異常是針對方法來說的,拋出、聲明拋出、捕獲和處理異常都是在方法中進行的。 Java異常處理通過5個關鍵字try、catch、throw、throws、finally進行管理。基本過程是用try語句塊包住要監視的語句,如果在try語句塊內出現異常,則異常會被拋出,你的代碼在catch語句塊中可以捕獲到這個異常並做處理;還有以部分系統生成的異常在Java運行時自動拋出。你也可以通過throws關鍵字在方法上聲明該方法要拋出異常,然後在方法內部通過throw拋出異常對象。finally語句塊會在方法執行return之前執行,一般結構如下: try{ 程序代碼 }catch(異常類型1 異常的變數名1){ 程序代碼 }catch(異常類型2 異常的變數名2){ 程序代碼 }finally{ 程序代碼 } catch語句可以有多個,用來匹配多個異常,匹配上多個中一個後,執行catch語句塊時候僅僅執行匹配上的異常。catch的類型是Java語言中定義的或者程序員自己定義的,表示代碼拋出異常的類型,異常的變數名表示拋出異常的對象的引用,如果catch捕獲並匹配上了該異常,那麼就可以直接用這個異常變數名,此時該異常變數名指向所匹配的異常,並且在catch代碼塊中可以直接引用。這一點非常非常的特殊和重要! Java異常處理的目的是提高程序的健壯性,你可以在catch和finally代碼塊中給程序一個修正機會,使得程序不因異常而終止或者流程發生以外的改變。同時,通過獲取Java異常信息,也為程序的開發維護提供了方便,一般通過異常信息就很快就能找到出現異常的問題(代碼)所在。 Java異常處理是Java語言的一大特色,也是個難點,掌握異常處理可以讓寫的代碼更健壯和易於維護。
二、Java異常類類圖
下面是這幾個類的層次圖: java.lang.Object java.lang.Throwable java.lang.Exception java.lang.RuntimeException java.lang.Error java.lang.ThreadDeath
下面四個類的介紹來自java api 文檔。
1、Throwable Throwable 類是 Java 語言中所有錯誤或異常的超類。只有當對象是此類(或其子類之一)的實例時,才能通過 Java 虛擬機或者 Java throw 語句拋出。類似地,只有此類或其子類之一才可以是 catch 子句中的參數類型。 兩個子類的實例,Error 和 Exception,通常用於指示發生了異常情況。通常,這些實例是在異常情況的上下文中新近創建的,因此包含了相關的信息(比如堆棧跟蹤數據)。
2、Exception Exception 類及其子類是 Throwable 的一種形式,它指出了合理的應用程序想要捕獲的條件,表示程序本身可以處理的異常。
3、Error Error 是 Throwable 的子類,表示僅靠程序本身無法恢復的嚴重錯誤,用於指示合理的應用程序不應該試圖捕獲的嚴重問題。 在執行該方法期間,無需在方法中通過throws聲明可能拋出但沒有捕獲的 Error 的任何子類,因為Java編譯器不去檢查它,也就是說,當程序中可能出現這類異常時,即使沒有用try...catch語句捕獲它,也沒有用throws字句聲明拋出它,還是會編譯通過。
4、RuntimeException RuntimeException 是那些可能在 Java 虛擬機正常運行期間拋出的異常的超類。Java編譯器不去檢查它,也就是說,當程序中可能出現這類異常時,即使沒有用try...catch語句捕獲它,也沒有用throws字句聲明拋出它,還是會編譯通過,這種異常可以通過改進代碼實現來避免。
5、ThreadDeath 調用 Thread 類中帶有零參數的 stop 方法時,受害線程將拋出一個 ThreadDeath 實例。 僅當應用程序在被非同步終止後必須清除時才應該捕獲這個類的實例。如果 ThreadDeath 被一個方法捕獲,那麼將它重新拋出非常重要,因為這樣才能讓該線程真正終止。 如果沒有捕獲 ThreadDeath,則頂級錯誤處理程序不會輸出消息。 雖然 ThreadDeath 類是「正常出現」的,但它只能是 Error 的子類而不是 Exception 的子類,因為許多應用程序捕獲所有出現的 Exception,然後又將其放棄。
以上是對有關異常API的一個簡單介紹,用法都很簡單,關鍵在於理解異常處理的原理,具體用法參看Java API文檔。
三、Java異常處理機制
對於可能出現異常的代碼,有兩種處理辦法: 第一、在方法中用try...catch語句捕獲並處理異常,catach語句可以有多個,用來匹配多個異常。例如: public void p(int x){ try{ ... }catch(Exception e){ ... }finally{ ... } }
第二、對於處理不了的異常或者要轉型的異常,在方法的聲明處通過throws語句拋出異常。例如: public void test1() throws MyException{ ... if(....){ throw new MyException(); } } 如果每個方法都是簡單的拋出異常,那麼在方法調用方法的多層嵌套調用中,Java虛擬機會從出現異常的方法代碼塊中往回找,直到找到處理該異常的代碼塊為止。然後將異常交給相應的catch語句處理。如果Java虛擬機追溯到方法調用棧最底部main()方法時,如果仍然沒有找到處理異常的代碼塊,將按照下面的步驟處理: 第一、調用異常的對象的printStackTrace()方法,列印方法調用棧的異常信息。 第二、如果出現異常的線程為主線程,則整個程序運行終止;如果非主線程,則終止該線程,其他線程繼續運行。 通過分析思考可以看出,越早處理異常消耗的資源和時間越小,產生影響的范圍也越小。因此,不要把自己能處理的異常也拋給調用者。 還有一點,不可忽視:finally語句在任何情況下都必須執行的代碼,這樣可以保證一些在任何情況下都必須執行代碼的可靠性。比如,在資料庫查詢異常的時候,應該釋放JDBC連接等等。finally語句先於return語句執行,而不論其先後位置,也不管是否try塊出現異常。finally語句唯一不被執行的情況是方法執行了System.exit()方法。System.exit()的作用是終止當前正在運行的 Java 虛擬機。finally語句塊中不能通過給變數賦新值來改變return的返回值,也建議不要在finally塊中使用return語句,沒有意義還容易導致錯誤。
最後還應該注意一下異常處理的語法規則: 第一、try語句不能單獨存在,可以和catch、finally組成 try...catch...finally、try...catch、try...finally三種結構,catch語句可以有一個或多個,finally語句最多一個,try、catch、finally這三個關鍵字均不能單獨使用。 第二、try、catch、finally三個代碼塊中變數的作用域分別獨立而不能相互訪問。如果要在三個塊中都可以訪問,則需要將變數定義到這些塊的外面。 第三、多個catch塊時候,Java虛擬機會匹配其中一個異常類或其子類,就執行這個catch塊,而不會再執行別的catch塊。 第四、throw語句後不允許有緊跟其他語句,因為這些沒有機會執行。 第五、如果一個方法調用了另外一個聲明拋出異常的方法,那麼這個方法要麼處理異常,要麼聲明拋出。
那怎麼判斷一個方法可能會出現異常呢?一般來說,方法聲明的時候用了throws語句,方法中有throw語句,方法調用的方法聲明有throws關鍵字。
throw和throws關鍵字的區別 throw用來拋出一個異常,在方法體內。語法格式為:throw 異常對象。 throws用來聲明方法可能會拋出什麼異常,在方法名後,語法格式為:throws 異常類型1,異常類型2...異常類型n。
四、如何定義和使用異常類
1、使用已有的異常類,假如為IOException、SQLException。 try{ 程序代碼 }catch(IOException ioe){ 程序代碼 }catch(SQLException sqle){ 程序代碼 }finally{ 程序代碼 }
2、自定義異常類 創建Exception或者RuntimeException的子類即可得到一個自定義的異常類。例如: public class MyException extends Exception{ public MyException(){} public MyException(String smg){ super(smg); } }
3、使用自定義的異常 用throws聲明方法可能拋出自定義的異常,並用throw語句在適當的地方拋出自定義的異常。例如: 在某種條件拋出異常 public void test1() throws MyException{ ... if(....){ throw new MyException(); } }
將異常轉型(也叫轉譯),使得異常更易讀易於理解 public void test2() throws MyException{ ... try{ ... }catch(SQLException e){ ... throw new MyException(); } }
還有一個代碼,很有意思: public void test2() throws MyException{ ... try { ... } catch (MyException e) { throw e; } }
這段代碼實際上捕獲了異常,然後又和盤托出,沒有一點意義,如果這樣還有什麼好處理的,不處理就行了,直接在方法前用throws聲明拋出不就得了。異常的捕獲就要做一些有意義的處理。
五、運行時異常和受檢查異常
Exception類可以分為兩種:運行時異常和受檢查異常。 1、運行時異常 RuntimeException類及其子類都被稱為運行時異常,這種異常的特點是Java編譯器不去檢查它,也就是說,當程序中可能出現這類異常時,即使沒有用try...catch語句捕獲它,也沒有用throws字句聲明拋出它,還是會編譯通過。例如,當除數為零時,就會拋出java.lang.ArithmeticException異常。 2、受檢查異常 除了RuntimeException類及其子類外,其他的Exception類及其子類都屬於受檢查異常,這種異常的特點是要麼用try...catch捕獲處理,要麼用throws語句聲明拋出,否則編譯不會通過。 3、兩者的區別 運行時異常表示無法讓程序恢復運行的異常,導致這種異常的原因通常是由於執行了錯誤的操作。一旦出現錯誤,建議讓程序終止。 受檢查異常表示程序可以處理的異常。如果拋出異常的方法本身不處理或者不能處理它,那麼方法的調用者就必須去處理該異常,否則調用會出錯,連編譯也無法通過。當然,這兩種異常都是可以通過程序來捕獲並處理的,比如除數為零的運行時異常: public class HelloWorld { public static void main(String[] args) { System.out.println("Hello World!!!"); try{ System.out.println(1/0); }catch(ArithmeticException e){ System.out.println("除數為0!"); } System.out.println("除數為零後程序沒有終止啊,呵呵!!!"); } }
運行結果:
Hello World!!! 除數為0! 除數為零後程序沒有終止啊,呵呵!!!
4、運行時錯誤 Error類及其子類表示運行時錯誤,通常是由Java虛擬機拋出的,JDK中與定義了一些錯誤類,比如VirtualMachineError 和OutOfMemoryError,程序本身無法修復這些錯誤.一般不去擴展Error類來創建用戶自定義的錯誤類。而RuntimeException類表示程序代碼中的錯誤,是可擴展的,用戶可以創建特定運行時異常類。 Error(運行時錯誤)和運行時異常的相同之處是:Java編譯器都不去檢查它們,當程序運行時出現它們,都會終止運行。
5、最佳解決方案 對於運行時異常,我們不要用try...catch來捕獲處理,而是在程序開發調試階段,盡量去避免這種異常,一旦發現該異常,正確的做法就會改進程序設計的代碼和實現方式,修改程序中的錯誤,從而避免這種異常。捕獲並處理運行時異常是好的解決辦法,因為可以通過改進代碼實現來避免該種異常的發生。 對於受檢查異常,沒說的,老老實實去按照異常處理的方法去處理,要麼用try...catch捕獲並解決,要麼用throws拋出! 對於Error(運行時錯誤),不需要在程序中做任何處理,出現問題後,應該在程序在外的地方找問題,然後解決。
六、異常轉型和異常鏈 異常轉型在上面已經提到過了,實際上就是捕獲到異常後,將異常以新的類型的異常再拋出,這樣做一般為了異常的信息更直觀!比如: public void run() throws MyException{ ... try{ ... }catch(IOException e){ ... throw new MyException(); }finally{ ... } }
異常鏈,在JDK1.4以後版本中,Throwable類支持異常鏈機制。Throwable 包含了其線程創建時線程執行堆棧的快照。它還包含了給出有關錯誤更多信息的消息字元串。最後,它還可以包含 cause(原因):另一個導致此 throwable 拋出的 throwable。它也稱為異常鏈 設施,因為 cause 自身也會有 cause,依此類推,就形成了異常鏈,每個異常都是由另一個異常引起的。 通俗的說,異常鏈就是把原始的異常包裝為新的異常類,並在新的異常類中封裝了原始異常類,這樣做的目的在於找到異常的根本原因。
通過Throwable的兩個構造方法可以創建自定義的包含異常原因的異常類型: Throwable(String message, Throwable cause) 構造一個帶指定詳細消息和 cause 的新 throwable。 Throwable(Throwable cause) 構造一個帶指定 cause 和 (cause==null ? null :cause.toString())(它通常包含類和 cause 的詳細消息)的詳細消息的新 throwable。 getCause() 返回此 throwable 的 cause;如果 cause 不存在或未知,則返回 null。 initCause(Throwable cause) 將此 throwable 的 cause 初始化為指定值。 在Throwable的子類Exception中,也有類似的指定異常原因的構造方法: Exception(String message, Throwable cause) 構造帶指定詳細消息和原因的新異常。 Exception(Throwable cause) 根據指定的原因和 (cause==null ? null : cause.toString()) 的詳細消息構造新異常(它通常包含 cause 的類和詳細消息)。 因此,可以通過擴展Exception類來構造帶有異常原因的新的異常類。
七、Java異常處理的原則和技巧
1、避免過大的try塊,不要把不會出現異常的代碼放到try塊裡面,盡量保持一個try塊對應一個或多個異常。 2、細化異常的類型,不要不管什麼類型的異常都寫成Excetpion。 3、catch塊盡量保持一個塊捕獲一類異常,不要忽略捕獲的異常,捕獲到後要麼處理,要麼轉譯,要麼重新拋出新類型的異常。 4、不要把自己能處理的異常拋給別人。 5、不要用try...catch參與控製程序流程,異常控制的根本目的是處理程序的非正常情況。
㈢ 常見JAVA運行時異常有哪些
1,java.lang.NullPointerException
這個異常的解釋是 "程序遇上了空指針 ",簡單地說就是調用了未經初始化的對象或者是不存在的對象,這個錯誤經常出現在創建圖片,調用數組這些操作中,比如圖片未經初始化,或者圖片創建時的路徑錯誤等等。
2,java.lang.ClassNotFoundException
異常的解釋是"指定的類不存在",這里主要考慮一下類的名稱和路徑是否正確即可
3,java.lang.
這個異常的解釋是"數組下標越界",現在程序中大多都有對數組的操作,因此在調用數組的時候一定要認真檢查,看自己調用的下標是不是超出了數組的范圍,一般來說,顯示(即直接用常數當下標)調用不太容易出這樣的錯,但隱式(即用變數表示下標)調用就經常出錯了.
4,java.lang.NoSuchMethodError
方法不存在錯誤。當應用試圖調用某類的某個方法,而該類的定義中沒有該方法的定義時拋出該錯誤。
5,java.lang.IndexOutOfBoundsException
索引越界異常。當訪問某個序列的索引值小於0或大於等於序列大小時,拋出該異常。
6,java.lang.NumberFormatException
數字格式異常。當試圖將一個String轉換為指定的數字類型,而該字元串確不滿足數字類型要求的格式時,拋出該異常。
7,java.sql.SQLException
Sql語句執行異常
8,java.io.IOException
輸入輸出異常
9,java.lang.IllegalArgumentException
方法參數錯誤
10java.lang.IllegalAccessException
無訪問許可權異常
(3)java異常框架擴展閱讀:
Java技術應用領域:
1、Android應用
許多的 Android應用都是Java程序員開發者開發。雖然 Android運用了不同的JVM以及不同的封裝方式,但是代碼還是用Java語言所編寫。相當一部分的手機中都支持JAVA游戲,這就使很多非編程人員都認識了JAVA。
2、在金融業應用的伺服器程序
Java在金融服務業的應用非常廣泛,很多第三方交易系統、銀行、金融機構都選擇用Java開發,因為相對而言,Java較安全 。大型跨國投資銀行用Java來編寫前台和後台的電子交易系統,結算和確認系統,數據處理項目以及其他項目。
3、網站
Java 在電子商務領域以及網站開發領域占據了一定的席位。開發人員可以運用許多不同的框架來創建web項目,SpringMVC,Struts2.0以及frameworks。即使是簡單的 servlet,jsp和以struts為基礎的網站在政府項目中也經常被用到。例如醫療救護、保險、教育、國防以及其他的不同部門網站都是以Java為基礎來開發的。
4、嵌入式領域
Java在嵌入式領域發展空間很大。在這個平台上,只需130KB就能夠使用Java技術(在智能卡或者感測器上)。
5、大數據技術
Hadoop以及其他大數據處理技術很多都是用Java,例如Apache的基於Java的HBase和Accumulo以及 ElasticSearchas。
6、高頻交易的空間
Java平台提高了這個平台的特性和即使編譯,他同時也能夠像 C++ 一樣傳遞數據。正是由於這個原因,Java成為的程序員編寫交易平台的語言,因為雖然性能不比C++,但開發人員可以避開安全性,可移植性和可維護性等問題。
7、科學應用
Java在科學應用中是很好選擇,包括自然語言處理。最主要的原因是因為Java比C++或者其他語言相對其安全性、便攜性、可維護性以及其他高級語言的並發性更好。
㈣ Java異常機制是什麼
一、異常的關鍵字:
一般來說,異常的關鍵字有:try、catch、finally、throw、throws。
網上的資料對這幾個關鍵字是這樣解釋的:
try: Opening exception-handling statement.
catch: Captures the exception.
finally: Runs its code before terminating the program.
throws: Lists the exceptions a method could throw.
Throw: Transfers control of the method to the exception handler.
try語句
try語句用大括弧{}指定了一段代碼,該段代碼可能會拋棄一個或多個例外。
catch語句
catch語句的參數類似於方法的聲明,包括一個例外類型和一個例外對象。例外類型必須為Throwable類的子類,它指明了catch語句所處理的例外類型,例外對象則由運行時系統在try所指定的代碼塊中生成並被捕獲,大括弧中包含對象的處理,其中可以調用對象的方法。
catch語句可以有多個,分別處理不同類的例外。Java運行時系統從上到下分別對每個catch語句處理的例外類型進行檢測,直到找到類型相匹配的catch語句為止。這里,類型匹配指catch所處理的例外類型與生成的例外對象的類型完全一致或者是它的父類,因此,catch語句的排列順序應該是從特殊到一般。也可以用一個catch語句處理多個例外類型,這時它的例外類型參數應該是這多個例外類型的父類,程序設計中要根據具體的情況來選擇catch語句的例外處理類型。
finally語句
try所限定的代碼中,當拋棄一個例外時,其後的代碼不會被執行。通過finally語句可以指定一塊代碼。無論try所指定的程序塊中拋棄或不拋棄例外,也無論catch語句的例外類型是否與所拋棄的例外的類型一致,finally所指定的代碼都要被執行,它提供了統一的出口。通常在finally語句中可以進行資源的清除工作。如關閉打開的文件等。
throws語句
throws總是出現在一個函數頭中,用來標明該成員函數可能拋出的各種異常。對大多數Exception子類來說,Java 編譯器會強迫你聲明在一個成員函數中拋出的異常的類型。如果異常的類型是Error或 RuntimeException, 或它們的子類,這個規則不起作用, 因為這在程序的正常部分中是不期待出現的。 如果你想明確地拋出一個RuntimeException,你必須用throws語句來聲明它的類型
throw語句
throw總是出現在函數體中,用來拋出一個異常。程序會在throw語句後立即終止,它後面的語句執行不到,然後在包含它的所有try塊中(可能在上層調用函數中)從里向外尋找含有與其匹配的catch子句的try塊。
其實,我個人覺得,簡單的來說:throws與throw從拼寫上只相差一個s,但是功能、作用上有很大的區別。throws用於在方法和類處聲明可能拋出的所有異常信息。throw而throw就是單個語句拋出異常,是指拋出的一個具體的異常類型,使用在方法(類)的內部。
如:
………………………………………………………………………………………………………
public class showUI throws Exception(){
public void tbstudy throws Exception(){
****;//
try{
/* 這里是要處理的異常 */
}
Catch(Exception of){
System.out.println(of);//列印出異常
}
}
}
………………………………………………………………………………………………………
throws通常不用顯示的捕獲異常,可由系統自動將所有捕獲的異常信息拋給上級方法(即調用該方法或類的所有地方);
throw則需要用戶自己捕獲相關的異常,而後再對其進行相關處理(如列印異常的地方,類型等),最後將處理後的異常信息拋出。
他們對異常處理方式也不同.throws對異常不處理,誰調用誰處理,throws的Exception的取值范圍要大於方法內部異常的最大范圍,而cathch的范圍又要大於throws的Exception的范圍;throw 主動拋出自定義異常類對象。
二、異常繼承體系
異常的繼承結構
三、java處理異常方式
在java代碼中如果發生異常,jvm(java虛擬機)會拋出異常對象,導致程序代碼中斷,這個時候jvm在做的操作就是:創建異常對象,然後拋出,比如:
1.int i= 1;
2.int j = 0;
3.int res = 0;
4.res = i/j;//除0錯誤
5.System.out.println(res);
這5句代碼運行到第四句會中斷,因為jvm拋出了異常
2.throw的作用:手動拋出異常。有時候有些錯誤在jvm看來不是錯誤,比如:
1. int age = 0;
2. age = -100;
3.System.out.println(age);
很正常的整形變數賦值,但是在我們眼中看來就不正常,誰的年齡會是負的呢?!所以我們需要自己手動引發異常,這就是throw的作用
int age = 0;
age = -100;
if(age<0){
Exception e = new Exception(); //創建異常對象
throw e; //拋出異常
}
System.out.println(age);
java中的異常機制
異常機制是指當程序出現錯誤後,程序如何處理。具體來說,異常機制提供了程序退出的安全通道。當出現錯誤後,程序執行的流程發生改變,程序的控制權轉移到異常處理器。
傳統的處理異常的辦法是,函數返回一個特殊的結果來表示出現異常(通常這個特殊結果是大家約定俗稱的),調用該函數的程序負責檢查並分析函數返回的結果。這樣做有如下的弊端:例如函數返回-1代表出現異常,但是如果函數確實要返回-1這個正確的值時就會出現混淆;可讀性降低,將程序代碼與處理異常的代碼混疊在一起;由調用函數的程序來分析錯誤,這就要求客戶程序員對庫函數有很深的了解。
在使用File類的方法時,如正在將U盤裡面的照片復制到電腦里時,有人將U盤拔掉了。這時我們的復製程序就會出錯,即拋出異常。當出現程序無法控制的外部環境問題(用戶提供的文件不存在或者創建文件時已有同名文件存在,文件內容損壞,網路不可用...)時,JAVA就會用異常對象來描述。
異常情況通常有三大類:
(1)檢查性異常:java.lang.Exception
(2)運行期異常:java.lang.RuntimeException
(3)錯誤:java.lang.Error
它們都是java.lang.Throwable類的子孫類。如右圖:
Throwable 類是 Java 語言中所有錯誤和異常類的父類,對於具體的異常,不應該使用Throwable類,而應該使用其他三者之一。
檢查性異常------程序正確,但因為外在的環境條件不滿足引發。例如:用戶錯誤及I/O問題----程序試圖打開一個並不存在的遠程Socket埠。這不是程序本身的邏輯錯誤,而很可能是遠程機器名字錯誤(用戶拼寫錯誤)。對商用軟體系統,程序開發者必須考慮並處理這個問題。JAVA編譯器強制要求處理這類異常,如果不捕獲這類異常,程序將不能被編譯。
運行期異常------這意味著程序存在bug,如數組越界,0被除,入參不滿足規范.....這類異常需要更改程序來避免,JAVA編譯器強制要求處理這類異常。用來表示設計或實現方面的問題,如數組越界等。因為設計和實現正確的程序不會引發這類異常,所以常常不處理它。發生這類異常時,運行時環境會輸出一條信息,提示用戶修正錯誤。
錯誤------一般很少見,也很難通過程序解決。它可能源於程序的bug,但一般更可能源於環境問題,如內存耗盡。錯誤在程序中無須處理,而有運行環境處理。Error表示很難恢復的錯誤,如內存越界。一般不期望用戶程序來處理,即使程序員有能力處理這種錯誤,也還是交給系統處理為好。
㈤ java 目前市面上比較火的框架有哪些
Java 始終排在第一位,這使它成為有史以來最著名的軟體編程語言之一。及時的更新和新版本發布使它成為一種充滿活力的、有競爭力的編程語言。
2020年最常用的java框架
十大常用框架:
一、SpringMVC
二、Spring
三、Mybatis
四、Dubbo
五、Maven
六、RabbitMQ
七、Log4j
八、Ehcache
九、Redis
十、Shiro
一、SpringMVC
Spring Web MVC是一種基於Java的實現了Web MVC設計模式的請求驅動類型的輕量級Web框架,即使用了MVC架構模式的思想,將web層進行職責解耦,基於請求驅動指的就是使用請求-響應模型,框架的目的就是幫助我們簡化開發,Spring Web MVC也是要簡化我們日常Web開發的。
模型(Model )封裝了應用程序的數據和一般他們會組成的POJO。
視圖(View)是負責呈現模型數據和一般它生成的HTML輸出,客戶端的瀏覽器能夠解釋。
控制器(Controller )負責處理用戶的請求,並建立適當的模型,並把它傳遞給視圖渲染。
Spring的web模型 - 視圖 - 控制器(MVC)框架是圍繞著處理所有的HTTP請求和響應的DispatcherServlet的設計。
Spring Web MVC處理請求的流程
具體執行步驟如下:
1、 首先用戶發送請求————>前端控制器,前端控制器根據請求信息(如URL)來決定選擇哪一個頁面控制器進行處理並把請求委託給它,即以前的控制器的控制邏輯部分;圖2-1中的1、2步驟;
2、 頁面控制器接收到請求後,進行功能處理,首先需要收集和綁定請求參數到一個對象,這個對象在Spring Web MVC中叫命令對象,並進行驗證,然後將命令對象委託給業務對象進行處理;處理完畢後返回一個ModelAndView(模型數據和邏輯視圖名);圖2-1中的3、4、5步驟;
3、 前端控制器收回控制權,然後根據返回的邏輯視圖名,選擇相應的視圖進行渲染,並把模型數據傳入以便視圖渲染;圖2-1中的步驟6、7;
4、 前端控制器再次收回控制權,將響應返回給用戶,圖2-1中的步驟8;至此整個結束。
二、Spring
2.1、IOC容器:
IOC容器就是具有依賴注入功能的容器,IOC容器負責實例化、定位、配置應用程序中的對象及建立這些對象間的依賴。應用程序無需直接在代碼中new相關的對象,應用程序由IOC容器進行組裝。在Spring中BeanFactory是IOC容器的實際代表者。
2.2、AOP:
簡單地說,就是將那些與業務無關,卻為業務模塊所共同調用的邏輯或責任封裝起來,便於減少系統的重復代碼,降低模塊間的耦合度,並有利於未來的可操作性和可維護性。AOP代表的是一個橫向的關系
AOP用來封裝橫切關注點,具體可以在下面的場景中使用:
Authentication 許可權
Caching 緩存
Context passing 內容傳遞
Error handling 錯誤處理
Lazy loading懶載入
Debugging 調試
logging, tracing, profiling and monitoring記錄跟蹤優化校準
Performance optimization性能優化
Persistence 持久化
Resource pooling資源池
Synchronization同步
Transactions 事務
三、Mybatis
MyBatis 是支持普通 SQL查詢,存儲過程和高級映射的優秀持久層框架。MyBatis 消除了幾乎所有的JDBC代碼和參數的手工設置以及結果集的檢索。MyBatis 使用簡單的 XML或註解用於配置和原始映射,將介面和 Java 的POJOs(Plain Old Java Objects,普通的 Java對象)映射成資料庫中的記錄。
總體流程:
(1)載入配置並初始化
觸發條件:載入配置文件
將SQL的配置信息載入成為一個個MappedStatement對象(包括了傳入參數映射配置、執行的SQL語句、結果映射配置),存儲在內存中。
(2)接收調用請求
觸發條件:調用Mybatis提供的API
傳入參數:為SQL的ID和傳入參數對象
處理過程:將請求傳遞給下層的請求處理層進行處理。
(3)處理操作請求
觸發條件:API介面層傳遞請求過來
傳入參數:為SQL的ID和傳入參數對象
處理過程:
(A)根據SQL的ID查找對應的MappedStatement對象。
(B)根據傳入參數對象解析MappedStatement對象,得到最終要執行的SQL和執行傳入參數。
©獲取資料庫連接,根據得到的最終SQL語句和執行傳入參數到資料庫執行,並得到執行結果。
(D)根據MappedStatement對象中的結果映射配置對得到的執行結果進行轉換處理,並得到最終的處理結果。
(E)釋放連接資源。
(4)返回處理結果將最終的處理結果返回
MyBatis 最強大的特性之一就是它的動態語句功能。如果您以前有使用JDBC或者類似框架的經歷,您就會明白把SQL語句條件連接在一起是多麼的痛苦,要確保不能忘記空格或者不要在columns列後面省略一個逗號等。動態語句能夠完全解決掉這些痛苦。
四、Dubbo
Dubbo是一個分布式服務框架,致力於提供高性能和透明化的RPC(遠程過程調用協議)遠程服務調用方案,以及SOA服務治理方案。簡單的說,bbo就是個服務框架,如果沒有分布式的需求,其實是不需要用的,只有在分布式的時候,才有bbo這樣的分布式服務框架的需求,並且本質上是個服務調用的東東,說白了就是個遠程服務調用的分布式框架。
1、透明化的遠程方法調用,就像調用本地方法一樣調用遠程方法,只需簡單配置,沒有任何API侵入。
2、軟負載均衡及容錯機制,可在內網替代F5等硬體負載均衡器,降低成本,減少單點。
3、 服務自動注冊與發現,不再需要寫死服務提供方地址,注冊中心基於介面名查詢服務提供者的IP地址,並且能夠平滑添加或刪除服務提供者。
節點角色說明:
Provider: 暴露服務的服務提供方。
Consumer: 調用遠程服務的服務消費方。
Registry: 服務注冊與發現的注冊中心。
Monitor: 統計服務的調用次調和調用時間的監控中心。
Container: 服務運行容器。
五、Maven
Maven這個個項目管理和構建自動化工具,越來越多的開發人員使用它來管理項目中的jar包。但是對於我們程序員來說,我們最關心的是它的項目構建功能。
六、RabbitMQ
消息隊列一般是在項目中,將一些無需即時返回且耗時的操作提取出來,進行了非同步處理,而這種非同步處理的方式大大的節省了伺服器的請求響應時間,從而提高了系統的吞吐量。
RabbitMQ是用Erlang實現的一個高並發高可靠AMQP消息隊列伺服器。
Erlang是一門動態類型的函數式編程語言。對應到Erlang里,每個Actor對應著一個Erlang進程,進程之間通過消息傳遞進行通信。相比共享內存,進程間通過消息傳遞來通信帶來的直接好處就是消除了直接的鎖開銷(不考慮Erlang虛擬機底層實現中的鎖應用)。
AMQP(Advanced Message Queue Protocol)定義了一種消息系統規范。這個規范描述了在一個分布式的系統中各個子系統如何通過消息交互。
七、Log4j
日誌記錄的優先順序,分為OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者您定義的級別。
八、Ehcache
EhCache 是一個純Java的進程內緩存框架,具有快速、精乾等特點,是Hibernate中默認的CacheProvider。Ehcache是一種廣泛使用的開源Java分布式緩存。主要面向通用緩存,Java EE和輕量級容器。它具有內存和磁碟存儲,緩存載入器,緩存擴展,緩存異常處理程序,一個gzip緩存servlet過濾器,支持REST和SOAP api等特點。
優點:
1、 快速
2、 簡單
3、 多種緩存策略
4、緩存數據有兩級:內存和磁碟,因此無需擔心容量問題
5、 緩存數據會在虛擬機重啟的過程中寫入磁碟
6、可以通過RMI、可插入API等方式進行分布式緩存
7、 具有緩存和緩存管理器的偵聽介面
8、支持多緩存管理器實例,以及一個實例的多個緩存區域
9、提供Hibernate的緩存實現
缺點:
1、使用磁碟Cache的時候非常佔用磁碟空間:這是因為DiskCache的演算法簡單,該演算法簡單也導致Cache的效率非常高。它只是對元素直接追加存儲。因此搜索元素的時候非常的快。如果使用DiskCache的,在很頻繁的應用中,很快磁碟會滿。
2、不能保證數據的安全:當突然kill掉java的時候,可能會產生沖突,EhCache的解決方法是如果文件沖突了,則重建cache。這對於Cache數據需要保存的時候可能不利。當然,Cache只是簡單的加速,而不能保證數據的安全。如果想保證數據的存儲安全,可以使用Bekeley DB Java Edition版本。這是個嵌入式資料庫。可以確保存儲安全和空間的利用率。
九、Redis
redis是一個key-value存儲系統。和Memcached類似,它支持存儲的value類型相對更多,包括string(字元串)、list(鏈表)、set(集合)、zset(sorted set –有序集合)和hash(哈希類型)。這些數據類型都支持push/pop、add/remove及取交集並集和差集及更豐富的操作,而且這些操作都是原子性的。在此基礎上,redis支持各種不同方式的排序。與memcached一樣,為了保證效率,數據都是緩存在內存中。區別的是redis會周期性的把更新的數據寫入磁碟或者把修改操作寫入追加的記錄文件,並且在此基礎上實現了master-slave(主從)同步。
Redis資料庫完全在內存中,使用磁碟僅用於持久性。相比許多鍵值數據存儲,Redis擁有一套較為豐富的數據類型。Redis可以將數據復制到任意數量的從伺服器。
1.2、Redis優點:
(1)異常快速:Redis的速度非常快,每秒能執行約11萬集合,每秒約81000+條記錄。
(2)支持豐富的數據類型:Redis支持最大多數開發人員已經知道像列表,集合,有序集合,散列數據類型。這使得它非常容易解決各種各樣的問題,因為我們知道哪些問題是可以處理通過它的數據類型更好。
(3)操作都是原子性:所有Redis操作是原子的,這保證了如果兩個客戶端同時訪問的Redis伺服器將獲得更新後的值。
(4)多功能實用工具:Redis是一個多實用的工具,可以在多個用例如緩存,消息,隊列使用(Redis原生支持發布/訂閱),任何短暫的數據,應用程序,如Web應用程序會話,網頁命中計數等。
1.3、Redis缺點:
(1)單線程
(2)耗內存
十、Shiro
Apache Shiro是Java的一個安全框架,旨在簡化身份驗證和授權。Shiro在JavaSE和JavaEE項目中都可以使用。它主要用來處理身份認證,授權,企業會話管理和加密等。Shiro的具體功能點如下:
(1)身份認證/登錄,驗證用戶是不是擁有相應的身份;
(2)授權,即許可權驗證,驗證某個已認證的用戶是否擁有某個許可權;即判斷用戶是否能做事情,常見的如:驗證某個用戶是否擁有某個角色。或者細粒度的驗證某個用戶對某個資源是否具有某個許可權;
(3)會話管理,即用戶登錄後就是一次會話,在沒有退出之前,它的所有信息都在會話中;會話可以是普通JavaSE環境的,也可以是如Web環境的;
(4)加密,保護數據的安全性,如密碼加密存儲到資料庫,而不是明文存儲;
(5)Web支持,可以非常容易的集成到Web環境;
Caching:緩存,比如用戶登錄後,其用戶信息、擁有的角色/許可權不必每次去查,這樣可以提高效率;
(6)shiro支持多線程應用的並發驗證,即如在一個線程中開啟另一個線程,能把許可權自動傳播過去;
(7)提供測試支持;
(8)允許一個用戶假裝為另一個用戶(如果他們允許)的身份進行訪問;
(9)記住我,這個是非常常見的功能,即一次登錄後,下次再來的話不用登錄了。
文字描述可能並不能讓猿友們完全理解具體功能的意思。下面我們以登錄驗證為例,向猿友們介紹Shiro的使用。至於其他功能點,猿友們用到的時候再去深究其用法也不遲。
十一、設計模式
這個算不上框架,可自行忽略,不過我認為設計模式的思想很有必要了解一下。
思想:
開閉原則:
開閉原則就是說對擴展開放,對修改關閉。在程序需要進行拓展的時候,不能去修改原有的代碼。
針對介面編程,針對介面編程,依賴於抽象而不依賴於具體。
盡量使用合成/聚合的方式,而不是使用繼承。
一個實體應當盡量少的與其他實體之間發生相互作用,使得系統功能模塊相對獨立。
使用多個隔離的介面,比使用單個介面要好。
里氏代換原則:
(1)子類的能力必須大於等於父類,即父類可以使用的方法,子類都可以使用。
(2)返回值也是同樣的道理。假設一個父類方法返回一個List,子類返回一個ArrayList,這當然可以。如果父類方法返回一個ArrayList,子類返回一個List,就說不通了。這里子類返回值的能力是比父類小的。
(3)還有拋出異常的情況。任何子類方法可以聲明拋出父類方法聲明異常的子類。 而不能聲明拋出父類沒有聲明的異常。
㈥ 在java中,異常處理的機制有哪幾種,分別是什麼
1 引子
try…catch…finally恐怕是大家再熟悉不過的語句了,而且感覺用起來也是很簡單,邏輯上似乎也是很容易理解。不過,我親自體驗的「教訓」告訴我,這個東西可不是想像中的那麼簡單、聽話。不信?那你看看下面的代碼,「猜猜」它執行後的結果會是什麼?不要往後看答案、也不許執行代碼看真正答案哦。如果你的答案是正確,那麼這篇文章你就不用浪費時間看啦。
package myExample.testException;
public class TestException {
public TestException() {
}
boolean testEx() throws Exception{
boolean ret = true;
try{
ret = testEx1();
}catch (Exception e){
System.out.println("testEx, catch exception");
ret = false;
throw e;
}finally{
System.out.println("testEx, finally; return value="+ret);
return ret;
}
}
boolean testEx1() throws Exception{
boolean ret = true;
try{
ret = testEx2();
if (!ret){
return false;
}
System.out.println("testEx1, at the end of try");
return ret;
}catch (Exception e){
System.out.println("testEx1, catch exception");
ret = false;
throw e;
}
finally{
System.out.println("testEx1, finally; return value="+ret);
return ret;
}
}
boolean testEx2() throws Exception{
boolean ret = true;
try{
int b=12;
int c;
for (int i=2;i>=-2;i--){
c=b/i;
System.out.println("i="+i);
}
return true;
}catch (Exception e){
System.out.println("testEx2, catch exception");
ret = false;
throw e;
}
finally{
System.out.println("testEx2, finally; return value="+ret);
return ret;
}
}
public static void main(String[] args) {
TestException testException1 = new TestException();
try{
testException1.testEx();
}catch(Exception e){
e.printStackTrace();
}
}
}
你的答案是什麼?是下面的答案嗎?
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
2 基礎知識
2.1 相關概念
例外是在程序運行過程中發生的異常事件,比如除0溢出、數組越界、文件找不到等,這些事件的發生將阻止程序的正常運行。為了加強程序的魯棒性,程序設計時,必須考慮到可能發生的異常事件並做出相應的處理。C語言中,通過使用if語句來判斷是否出現了例外,同時,調用函數通過被調用函數的返回值感知在被調用函數中產生的例外事件並進行處理。全程變數ErroNo常常用來反映一個異常事件的類型。但是,這種錯誤處理機制會導致不少問題。
Java通過面向對象的方法來處理例外。在一個方法的運行過程中,如果發生了例外,則這個方法生成代表該例外的一個對象,並把它交給運行時系統,運行時系統尋找相應的代碼來處理這一例外。我們把生成例外對象並把它提交給運行時系統的過程稱為拋棄(throw)一個例外。運行時系統在方法的調用棧中查找,從生成例外的方法開始進行回朔,直到找到包含相應例外處理的方法為止,這一個過程稱為捕獲(catch)一個例外。
2.2 Throwable類及其子類
用面向對象的方法處理例外,就必須建立類的層次。類 Throwable位於這一類層次的最頂層,只有它的後代才可以做為一個例外被拋棄。圖1表示了例外處理的類層次。
從圖中可以看出,類Throwable有兩個直接子類:Error和Exception。Error類對象(如動態連接錯誤等),由Java虛擬機生成並拋棄(通常,Java程序不對這類例外進行處理);Exception類對象是Java程序處理或拋棄的對象。它有各種不同的子類分別對應於不同類型的例外。其中類RuntimeException代表運行時由Java虛擬機生成的例外,如算術運算例外ArithmeticException(由除0錯等導致)、數組越界例外等;其它則為非運行時例外,如輸入輸出例外IOException等。Java編譯器要求Java程序必須捕獲或聲明所有的非運行時例外,但對運行時例外可以不做處理。
圖1 例外處理的類層次
2.3 異常處理關鍵字
Java的異常處理是通過5個關鍵字來實現的:try,catch,throw,throws,finally。JB的在線幫助中對這幾個關鍵字是這樣解釋的:
Throws: Lists the exceptions a method could throw.
Throw: Transfers control of the method to the exception handler.
Try: Opening exception-handling statement.
Catch: Captures the exception.
Finally: Runs its code before terminating the program.
2.3.1 try語句
try語句用大括弧{}指定了一段代碼,該段代碼可能會拋棄一個或多個例外。
2.3.2 catch語句
catch語句的參數類似於方法的聲明,包括一個例外類型和一個例外對象。例外類型必須為Throwable類的子類,它指明了catch語句所處理的例外類型,例外對象則由運行時系統在try所指定的代碼塊中生成並被捕獲,大括弧中包含對象的處理,其中可以調用對象的方法。
catch語句可以有多個,分別處理不同類的例外。Java運行時系統從上到下分別對每個catch語句處理的例外類型進行檢測,直到找到類型相匹配的catch語句為止。這里,類型匹配指catch所處理的例外類型與生成的例外對象的類型完全一致或者是它的父類,因此,catch語句的排列順序應該是從特殊到一般。
也可以用一個catch語句處理多個例外類型,這時它的例外類型參數應該是這多個例外類型的父類,程序設計中要根據具體的情況來選擇catch語句的例外處理類型。
2.3.3 finally語句
try所限定的代碼中,當拋棄一個例外時,其後的代碼不會被執行。通過finally語句可以指定一塊代碼。無論try所指定的程序塊中拋棄或不拋棄例外,也無論catch語句的例外類型是否與所拋棄的例外的類型一致,finally所指定的代碼都要被執行,它提供了統一的出口。通常在finally語句中可以進行資源的清除工作。如關閉打開的文件等。
2.3.4 throws語句
throws總是出現在一個函數頭中,用來標明該成員函數可能拋出的各種異常。對大多數Exception子類來說,Java 編譯器會強迫你聲明在一個成員函數中拋出的異常的類型。如果異常的類型是Error或 RuntimeException, 或它們的子類,這個規則不起作用, 因為這在程序的正常部分中是不期待出現的。 如果你想明確地拋出一個RuntimeException,你必須用throws語句來聲明它的類型。
2.3.5 throw語句
throw總是出現在函數體中,用來拋出一個異常。程序會在throw語句後立即終止,它後面的語句執行不到,然後在包含它的所有try塊中(可能在上層調用函數中)從里向外尋找含有與其匹配的catch子句的try塊。
3 關鍵字及其中語句流程詳解
3.1 try的嵌套
你可以在一個成員函數調用的外面寫一個try語句,在這個成員函數內部,寫另一個try語句保護其他代碼。每當遇到一個try語句,異常的框架就放到堆棧上面,直到所有的try語句都完成。如果下一級的try語句沒有對某種異常進行處理,堆棧就會展開,直到遇到有處理這種異常的try語句。下面是一個try語句嵌套的例子。
class MultiNest {
static void procere() {
try {
int a = 0;
int b = 42/a;
} catch(java.lang.ArithmeticException e) {
System.out.println("in procere, catch ArithmeticException: " + e);
}
}
public static void main(String args[]) {
try {
procere();
} catch(java.lang. Exception e) {
System.out.println("in main, catch Exception: " + e);
}
}
}
這個例子執行的結果為:
in procere, catch ArithmeticException: java.lang.ArithmeticException: / by zero
成員函數procere里有自己的try/catch控制,所以main不用去處理 ;當然如果如同最開始我們做測試的例子一樣,在procere中catch到異常時使用throw e;語句將異常拋出,那麼main當然還是能夠捕捉並處理這個procere拋出來的異常。例如在procere函數的catch中的System.out語句後面增加throw e;語句之後,執行結果就變為:
in procere, catch ArithmeticException: java.lang.ArithmeticException: / by zero
in main, catch Exception: java.lang.ArithmeticException: / by zero
3.2 try-catch程序塊的執行流程以及執行結果
相對於try-catch-finally程序塊而言,try-catch的執行流程以及執行結果還是比較簡單的。
首先執行的是try語句塊中的語句,這時可能會有以下三種情況:
1. 如果try塊中所有語句正常執行完畢,那麼就不會有其他的「動做」被執行,整個try-catch程序塊正常完成。
2. 如果try語句塊在執行過程中碰到異常V,這時又分為兩種情況進行處理:
² 如果異常V能夠被與try相應的catch塊catch到,那麼第一個catch到這個異常的catch塊(也是離try最近的一個與異常V匹配的catch塊)將被執行;如果catch塊執行正常,那麼try-catch程序塊的結果就是「正常完成」;如果該catch塊由於原因R突然中止,那麼try-catch程序塊的結果就是「由於原因R突然中止(completes abruptly)」。
² 如果異常V沒有catch塊與之匹配,那麼這個try-catch程序塊的結果就是「由於拋出異常V而突然中止(completes abruptly)」。
3. 如果try由於其他原因R突然中止(completes abruptly),那麼這個try-catch程序塊的結果就是「由於原因R突然中止(completes abruptly)」。
3.3 try-catch-finally程序塊的執行流程以及執行結果
try-catch-finally程序塊的執行流程以及執行結果比較復雜。
首先執行的是try語句塊中的語句,這時可能會有以下三種情況:
1. 如果try塊中所有語句正常執行完畢,那麼finally塊的居於就會被執行,這時分為以下兩種情況:
² 如果finally塊執行順利,那麼整個try-catch-finally程序塊正常完成。
² 如果finally塊由於原因R突然中止,那麼try-catch-finally程序塊的結局是「由於原因R突然中止(completes abruptly)」
2. 如果try語句塊在執行過程中碰到異常V,這時又分為兩種情況進行處理:
² 如果異常V能夠被與try相應的catch塊catch到,那麼第一個catch到這個異常的catch塊(也是離try最近的一個與異常V匹配的catch塊)將被執行;這時就會有兩種執行結果:
² 如果catch塊執行正常,那麼finally塊將會被執行,這時分為兩種情況:
² 如果finally塊執行順利,那麼整個try-catch-finally程序塊正常完成。
² 如果finally塊由於原因R突然中止,那麼try-catch-finally程序塊的結局是「由於原因R突然中止(completes abruptly)」
² 如果catch塊由於原因R突然中止,那麼finally模塊將被執行,分為兩種情況:
² 如果如果finally塊執行順利,那麼整個try-catch-finally程序塊的結局是「由於原因R突然中止(completes abruptly)」。
² 如果finally塊由於原因S突然中止,那麼整個try-catch-finally程序塊的結局是「由於原因S突然中止(completes abruptly)」,原因R將被拋棄。
(注意,這里就正好和我們的例子相符合,雖然我們在testEx2中使用throw e拋出了異常,但是由於testEx2中有finally塊,而finally塊的執行結果是complete abruptly的(別小看這個用得最多的return,它也是一種導致complete abruptly的原因之一啊——後文中有關於導致complete abruptly的原因分析),所以整個try-catch-finally程序塊的結果是「complete abruptly」,所以在testEx1中調用testEx2時是捕捉不到testEx1中拋出的那個異常的,而只能將finally中的return結果獲取到。
如果在你的代碼中期望通過捕捉被調用的下級函數的異常來給定返回值,那麼一定要注意你所調用的下級函數中的finally語句,它有可能會使你throw出來的異常並不能真正被上級調用函數可見的。當然這種情況是可以避免的,以testEx2為例:如果你一定要使用finally而且又要將catch中throw的e在testEx1中被捕獲到,那麼你去掉testEx2中的finally中的return就可以了。
這個事情已經在OMC2.0的MIB中出現過啦:伺服器的異常不能完全被反饋到客戶端。)
² 如果異常V沒有catch塊與之匹配,那麼finally模塊將被執行,分為兩種情況:
² 如果finally塊執行順利,那麼整個try-catch-finally程序塊的結局就是「由於拋出異常V而突然中止(completes abruptly)」。
² 如果finally塊由於原因S突然中止,那麼整個try-catch-finally程序塊的結局是「由於原因S突然中止(completes abruptly)」,異常V將被拋棄。
3. 如果try由於其他原因R突然中止(completes abruptly),那麼finally塊被執行,分為兩種情況:
² 如果finally塊執行順利,那麼整個try-catch-finally程序塊的結局是「由於原因R突然中止(completes abruptly)」。
² 如果finally塊由於原因S突然中止,那麼整個try-catch-finally程序塊的結局是「由於原因S突然中止(completes abruptly)」,原因R將被拋棄。
3.4 try-catch-finally程序塊中的return
從上面的try-catch-finally程序塊的執行流程以及執行結果一節中可以看出無論try或catch中發生了什麼情況,finally都是會被執行的,那麼寫在try或者catch中的return語句也就不會真正的從該函數中跳出了,它的作用在這種情況下就變成了將控制權(語句流程)轉到finally塊中;這種情況下一定要注意返回值的處理。
例如,在try或者catch中return false了,而在finally中又return true,那麼這種情況下不要期待你的try或者catch中的return false的返回值false被上級調用函數獲取到,上級調用函數能夠獲取到的只是finally中的返回值,因為try或者catch中的return語句只是轉移控制權的作用。
3.5 如何拋出異常
如果你知道你寫的某個函數有可能拋出異常,而你又不想在這個函數中對異常進行處理,只是想把它拋出去讓調用這個函數的上級調用函數進行處理,那麼有兩種方式可供選擇:
第一種方式:直接在函數頭中throws SomeException,函數體中不需要try/catch。比如將最開始的例子中的testEx2改為下面的方式,那麼testEx1就能捕捉到testEx2拋出的異常了。
boolean testEx2() throws Exception{
boolean ret = true;
int b=12;
int c;
for (int i=2;i>=-2;i--){
c=b/i;
System.out.println("i="+i);
}
return true;
}
第二種方式:使用try/catch,在catch中進行一定的處理之後(如果有必要的話)拋出某種異常。例如上面的testEx2改為下面的方式,testEx1也能捕獲到它拋出的異常:
boolean testEx2() throws Exception{
boolean ret = true;
try{
int b=12;
int c;
for (int i=2;i>=-2;i--){
c=b/i;
System.out.println("i="+i);
}
return true;
}catch (Exception e){
System.out.println("testEx2, catch exception");
Throw e;
}
}
第三種方法:使用try/catch/finally,在catch中進行一定的處理之後(如果有必要的話)拋出某種異常。例如上面的testEx2改為下面的方式,testEx1也能捕獲到它拋出的異常:
boolean testEx2() throws Exception{
boolean ret = true;
try{
int b=12;
int c;
for (int i=2;i>=-2;i--){
c=b/i;
System.out.println("i="+i);
throw new Exception("aaa");
}
return true;
}catch (java.lang.ArithmeticException e){
System.out.println("testEx2, catch exception");
ret = false;
throw new Exception("aaa");
}finally{
System.out.println("testEx2, finally; return value="+ret);
}
}
4 關於abrupt completion
前面提到了complete abruptly(暫且理解為「突然中止」或者「異常結束」吧),它主要包含了兩種大的情形:abrupt completion of expressions and statements,下面就分兩種情況進行解釋。
4.1 Normal and Abrupt Completion of Evaluation
每一個表達式(expression)都有一種使得其包含的計算得以一步步進行的正常模式,如果每一步計算都被執行且沒有異常拋出,那麼就稱這個表達式「正常結束(complete normally)」;如果這個表達式的計算拋出了異常,就稱為「異常結束(complete abruptly)」。異常結束通常有一個相關聯的原因(associated reason),通常也就是拋出一個異常V。
與表達式、操作符相關的運行期異常有:
² A class instance creation expression, array creation expression , or string concatenation operatior expression throws an OutOfMemoryError if there is insufficient memory available.
² An array creation expression throws a NegativeArraySizeException if the value of any dimension expression is less than zero.
² A field access throws a NullPointerException if the value of the object reference expression is null.
² A method invocation expression that invokes an instance method throws a NullPointerException if the target reference is null.
² An array access throws a NullPointerException if the value of the array reference expression is null.
² An array access throws an if the value of the array index expression is negative or greater than or equal to the length of the array.
² A cast throws a ClassCastException if a cast is found to be impermissible at run time.
² An integer division or integer remainder operator throws an ArithmeticException if the value of the right-hand operand expression is zero.
² An assignment to an array component of reference type throws an ArrayStoreException when the value to be assigned is not compatible with the component type of the array.
4.2 Normal and Abrupt Completion of Statements
正常情況我們就不多說了,在這里主要是列出了abrupt completion的幾種情況:
² break, continue, and return 語句將導致控制權的轉換,從而使得statements不能正常地、完整地執行。
² 某些表達式的計算也可能從java虛擬機拋出異常,這些表達式在上一小節中已經總結過了;一個顯式的的throw語句也將導致異常的拋出。拋出異常也是導致控制權的轉換的原因(或者說是阻止statement正常結束的原因)。
如果上述事件發生了,那麼這些statement就有可能使得其正常情況下應該都執行的語句不能完全被執行到,那麼這些statement也就是被稱為是complete abruptly.
導致abrupt completion的幾種原因:
² A break with no label
² A break with a given label
² A continue with no label
² A continue with a given label
² A return with no value
² A return with a given value A
² throw with a given value, including exceptions thrown by the Java virtual machine
5 關於我們的編程的一點建議
弄清楚try-catch-finally的執行情況後我們才能正確使用它。
如果我們使用的是try-catch-finally語句塊,而我們又需要保證有異常時能夠拋出異常,那麼在finally語句中就不要使用return語句了(finally語句塊的最重要的作用應該是釋放申請的資源),因為finally中的return語句會導致我們的throw e被拋棄,在這個try-catch-finally的外面將只能看到finally中的返回值(除非在finally中拋出異常)。(我們需要記住:不僅throw語句是abrupt completion 的原因,return、break、continue等這些看起來很正常的語句也是導致abrupt completion的原因。)