摘要:系列博文目錄編程思想學習錄連載之一切都是對象編程思想學習錄連載之初始化與清理編程思想學習錄連載之內部類編程思想學習錄連載之異常本篇文章將講述關于異常的相關知識注本文首發于公眾號,可長按或掃描下面的小心心來訂閱基本概念使用異常來提供一致性的錯
Thinking in java系列博文目錄:
Java編程思想學習錄(連載之:一切都是對象)
Java編程思想學習錄(連載之:初始化與清理)
Java編程思想學習錄(連載之:內部類)
Java編程思想學習錄(連載之:異常)
本篇文章將講述關于異常的相關知識
注: 本文首發于 My 公眾號 CodeSheep ,可 長按 或 掃描 下面的 小心心 來訂閱 ↓ ↓ ↓基本概念
Java使用異常來提供一致性的錯誤報告模型;且可集中錯誤處理;且任務代碼與異常代碼分割開來,易于理解和維護
雖然異常處理理論有終止模型、恢復模型兩種,但恢復模型很難優雅地做到,∴并不實用,實際中大家都是轉向使用終止模型代碼
一個異常拋出后發生的兩件事:① 使用new在堆上創建異常對象;② 異常處理機制開始接管流程(當前的執行流程被終止)
標準異常類均有兩個ctor:① default ctor; ② 帶字符串參數的ctor
Throwable是異常類型的根類
catch異常時,try中拋出的是子類異常,但catch的是基類異常也是OK,但若catch子類異常和基類異常的子句同時存在時,應將基類catch子句放在后面避免“屏蔽”現象發生
拋出異常 + 捕獲異常拋出異常(throw):
if( t==null ) throw new NullPointerException(); // 異常對象用new創建于堆上
捕獲異常(try+catch):
try { ... } catch( Type1 id1 ) { // 處理Type1類型的異常代碼 } catch( Type2 id2 ) { // 處理Type2類型的異常代碼 }
雖然上面的id1和id2在處理異常代碼中可能用不到,但不能少,必須定義
異常發生時,異常機制搜尋參數與異常類型相匹配的第一個catch子句并進入
創建自定義異常創建不帶參數ctor的自定義異常類:
// 自定義異常類(default ctor) class SimpleException extends Exception {} ------------------------------------------------------------ // 客戶端代碼 public class UseException { public void fun throws SimpleException { System.out.println( "Throw SimpleExcetion from fun" ); throw new SimpleException(); } public static void main( String[] args ) { UseException user = new UseException(); try { user.fun(); } catch( SimpleException e ) { System.out.println("Caught it !"); } } } ------------------------------------------------------------ // 輸出 Throw SimpleExcetion from fun Caught it !
創建帶參數ctor的自定義異常類
// 自定義異常類(有參ctor) class MyException extends Exception { public MyException() { } public MyException( String msg ) { super(msg); } } ------------------------------------------------------------ // 客戶端代碼 public class UseException { pubilc static void f() throws MyException { System.out.println( "Throwing MyException from f()" ) throw new MyException(); } public static void g() throws MyException { System.out.println( "Throwing MyException from g()" ) throw new MyException("Originated in g()"); } publib static void main( String[] args ) { try { f(); } catch( MyException e ) { e.printStackTrace( System.out ); } try { g(); } catch( MyException e ) { e.printStackTrace( System.out ); } } } ------------------------------------------------------------ // 輸出 Throwing MyException from f() MyException at ... at ... Throwing MyException from g() MyException: Originated in g() // 此即創建異常類型時傳入的String參數 at ... at ...捕獲所有異常
try { ... } catch( Exception e ) { // 填寫異常的基類,該catch子句一般置于末尾 ... }
Exception類型所持有的方法:
String getMessage()
String getLocalizedMessage()
String toString()
void printStackTrace()
void printStackTrace( PrintStream )
void printStackTrace( javo.io.PrintWriter )
注意:從下往上每個方法都比前一個提供了更多的異常信息!
棧軌跡printStackTrace()方法所提供的棧軌跡信息可以通過getStackTrace()方法來Get,舉例:
try { throw new Exception(); } catch( Exception e ) { for( StackTraceElement ste : e.getStackTrace() ) System.out.println( ste.getMethodName() ); }
這里使用getMethodName()方法來給出異常棧軌跡所經過的方法名!
重拋異常try { ... } catch( Exception e ) { throw e; // 重新拋出一個異常! }
若只是簡單地將異常重新拋出,則而后用printStackTrace()顯示的將是原異常拋出點的調用棧信息,而非重新拋出點的信息,欲更正該信息,可以使用fillInStackTrace()方法:
try { ... } catch( Exception e ) { throw (Exception)e.fillInStackTrace(); // 該行就成了異常的新發生地! }異常鏈
異常鏈:在捕獲一個異常后拋出另一個異常,并希望將原始的異常信息保存下來!
解決辦法:
在異常的ctor中加入cause參數
使用initCause()方法
注意:Throwable子類中,僅三種基本的異常類提供了待cause參數的ctor(Error、Exception、RuntimeException),其余情況只能靠initCause()方法,舉例:
class DynamicFieldsException extends Exception { } public Object setField( String id, Object value ) throws DynamicFieldsException { if( value == null ) { DynamicFieldsException dfe = new DynamicFieldsException(); dfe.initCause( new NullPointerException() ); throw dfe; } Object result = null; try { result = getField(id); } catch( NoSuchFieldException e ) { throw new RuntimeException( e ); } }Java標準異常
看這個圖需要明確:程序員一般關心Exception基類型的異常
由圖中可知,Error、RuntimeException都叫做“Unchecked Exception”,即不檢查異常,程序員也無需寫異常處理的代碼,這種自動捕獲
若諸如RuntimeException這種Unchecked異常沒有被捕獲而直達main(),則程序在退出前將自動調用異常的printStackTrace()方法
使用finally進行清理try { ... } catch(...) { ... } finally { // finally子句總是會被執行!!! ... }
使用時機:
當需要把內存之外的資源(如:文件句柄、網絡連接、某個外部世界的開關)恢復到初始狀態時!
try { ... } catch(...) { ... } finally { // finally子句總是會被執行!!! sw.off(); // 最后總是需要關掉某個開關! }
在return中使用finally
public static void func( int i ) { try { if( i==1 ) return; if( i==2 ) return; } finally { print( "Performing cleanup!" ); // 即使上面有很多return,但該句肯定被執行 } }
finally存在的缺憾:兩種情況下的finally使用會導致異常丟失!
前一個異常還未處理就拋出下一個異常
// 異常類 class VeryImportantException extends Exception { poublic String toString() { return "A verfy important exception!"; } } class HoHumException extends Exception { public String toString() { return "A trivial exception!"; } } ------------------------------------------------------------------ // 使用異常的客戶端 public class LostMessage { void f() throws VeryImportantException { throw new VeryImportantException(); } void dispose() throws HoHumException { throw new HoHumException(); } public static void main( String[] args ) { try { LostMessage lm = new LostMessage(); try { lm.f(); } finally { lm.dispose(); // 最后只會該異常生效,lm.f()拋出的異常丟了! } } catch( Exception e ) { System.out.println(e); } } } ----------------------------------------------------------------- // 輸出 A trivial exception!
finally子句中的return
public static void main( String[] args ) { try { throw new RuntimeException(); } finally { return; // 這將會掩蓋所有的異常拋出 } }繼承基類、實現接口時的異常限制
// 異常類 class A extends Exception { } class A1 extends A { } class A2 extends A { } class A1_1 extends A1 { } class B extends Exception { } class B1 extends B { } ------------------------------------------------- // 用了異常類的基類 abstract class Base { public Base() throws A { } public void event() throws A { } // (1) public abstract void atBat throws A1, A2; public void walk() { } } ------------------------------------------------- // 用了異常類的接口 interface Interf { public void event() throws B1; public void rainHard() throws B1; } ------------------------------------------------- // 繼承基類并實現接口的客戶端類 public class Ext extends Base implements Interf { public Ext() throws B1, A { } // (2) public Ext( String s ) throws A1, A {} // (2) public void walk() throws A1_1 { } // (3) 編譯錯誤! public void rainHard() throws B1 {} // (4) public void event() { } // (5) public void atBat() throws A1_1 { } // (6) public static void main( String[] args ) { try { Ext ext = new Ext(); ext.atBat(); } catch( A1_1 e ) { ... } catch( B1 e ) { ... } catch( A e ) { ... } try { Base base = new Ext(); ext.atBat(); } catch( A2 e ) { // 這里的catch必須按照Base中函數的異常拋出來寫 ... } catch( A1 e ) { ... } catch( B1 e ) { ... } catch( A ) { ... } } }
上面的例子可以總結如下:【注意對應數字標號】
(1) 基類的構造器或者方法聲明了拋出異常,但實際上沒有,這里相當于為繼承類寫了一個異常拋出規范,子類實現時安裝這個規范來拋異常
(2) 從這兩個ctor看出:異常限制對ctor不生效,子類ctor可以拋出任何異常而不管基類ctor所拋出的異常
(3) 基類函數沒拋異常,派生類重寫時不能瞎拋!
(4) 完全遵守基類的拋出,正常情況
(5) 基類函數拋了異常,派生類重寫時不拋也是OK的
(6) 派生類重寫基類函數時拋的異常可以是基類函數拋出異常的子類型
構造器中異常如何書寫對于在構造階段可能會拋出異常并要求清理的類,安全的方式是使用嵌套的try子句:即在創建需要清理的對象之后,立即進入一個try-finally塊,舉例:
特別需要注意的是下面的例子里在ctor中對文件句柄的close應放置的合理位置!
// 需要清理的對象類 class InputFile { private BufferedReader in; InputFile( String fname ) throws Exception { // 構造函數! try { in = new BufferedReader( new FileReader(fname) ); // 這里放置可能拋出異常的其他代碼 } catch( FileNotFoundException e ) { // 若上面的FileReader異常,將會拋FileNotFoundException,走到這里,該分支無需in.close()的 System.out.println( "Could not open " + fname ); throw e; } catch( Exception e ) { // 走到這里其實說明in對象已經構建成功,這里是必須in.close()的 try { in.close(); // 注意此處關閉動作多帶帶用try進行保障 } catch( IOException e2 ) { System.out.println("in.close() unsuccessful"); } throw e; } finally { // 注意in.close() 不要在此處關閉,因為try中假如BufferedReader構造失敗,此時in對象未生成成功,是無需close()一說的! } } String getLine() { String s; try { s = in.readLine(); } catch( IOException e ) { System.out.println( "readLine() unsuccessful!" ); s = "failed"; } return s; } void cleanup() { // 提供手動的關閉文件句柄的操作函數 try { in.close(); } catch( IOException e ) { System.out.println( "in.close() failed !" ); } } } ---------------------------------------------------- // 客戶端代碼 public class Cleanup { public static void main( String[] args ) { try { InputFile in = new InputFile( "Cleanup.java" ); try { // 上面InputFile構造完成以后立即進入該try-finally子句! String s = ""; int i = 1; while( (s = in.getLine()) != null ) System.out.println(""+ i++ + ": " + s); } catch( Exception e ) { e.printStackTrace( System.out ); } finally { // 該finally一定確保in能正常cleanup()! in.cleanup(); } } catch( Exception e ) { System.out.println( "InputFile ctor failed!" ); } } // end main() }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/68469.html
摘要:非內部類通過一個特殊的鏈接到其外圍類的對象,而類型的內部類無此引用。 showImg(https://segmentfault.com/img/remote/1460000012925199); 用thinkpad打字確實很爽啊! Thinking in java系列博文目錄: Java編程思想學習錄(連載之:一切都是對象) Java編程思想學習錄(連載之:初始化與清理) Java...
摘要:注本文首發于公眾號,可長按或掃描下面的小心心來訂閱關于構造器與初始化無參構造器默認構造器自己未寫編譯器幫忙自動創建的若自行定義了構造器無論參數有否,編譯器便停止默認創建動作類里的對象引用默認初始化為,基本類型初始化為構造器也是類的靜態方法四 showImg(https://segmentfault.com/img/remote/1460000015723687); 注: 本文首發于 ...
摘要:前端技術棧還是非常龐大的,為了能夠借助已經存在的輪子來造出一輛車,所以我選擇了進行實踐。狀態的管理的狀態管理依靠完成,用其來管理的所有組件狀態。私有云客戶端打造主頁面首先是主頁面,可以打開任何一個云主機系統的頁面看,基本類似。 showImg(https://segmentfault.com/img/remote/1460000013930354); 【利用K8S技術棧打造個人私有...
摘要:前端技術棧還是非常龐大的,為了能夠借助已經存在的輪子來造出一輛車,所以我選擇了進行實踐。狀態的管理的狀態管理依靠完成,用其來管理的所有組件狀態。私有云客戶端打造主頁面首先是主頁面,可以打開任何一個云主機系統的頁面看,基本類似。 showImg(https://segmentfault.com/img/remote/1460000013930354); 【利用K8S技術棧打造個人私有...
摘要:將用戶命令通過接口傳送給,從而進行資源的增刪改等操作。要使用編寫應用程序,當下大多語言都可以很方便地去實現請求來操作的接口從而控制和查詢資源,但本文主要是利用已有的客戶端來更加優雅地實現的資源控制。 showImg(https://segmentfault.com/img/remote/1460000013517345); 【利用K8S技術棧打造個人私有云系列文章目錄】 利用K8S...
閱讀 2609·2021-11-18 10:02
閱讀 2279·2021-09-30 09:47
閱讀 1745·2021-09-27 14:01
閱讀 3109·2021-08-16 11:00
閱讀 3163·2019-08-30 11:06
閱讀 2392·2019-08-29 17:29
閱讀 1531·2019-08-29 13:19
閱讀 445·2019-08-26 13:54