Thinking in java系列博文目錄:java
本篇文章將講述關於異常的相關知識程序員
注: 本文首發於 My 公衆號 CodeSheep ,可 長按 或 掃描 下面的 當心心 來訂閱 ↓ ↓ ↓編程
if( t==null )
throw new NullPointerException(); // 異常對象用new建立於堆上
複製代碼
try {
...
} catch( Type1 id1 ) {
// 處理Type1類型的異常代碼
} catch( Type2 id2 ) {
// 處理Type2類型的異常代碼
}
複製代碼
建立不帶參數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的自定義異常類bash
// 自定義異常類(有參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類型所持有的方法:網絡
注意:從下往上每一個方法都比前一個提供了更多的異常信息!函數
printStackTrace()方法所提供的棧軌跡信息能夠經過getStackTrace()方法來Get,舉例:學習
try {
throw new Exception();
} catch( Exception e ) {
for( StackTraceElement ste : e.getStackTrace() )
System.out.println( ste.getMethodName() );
}
複製代碼
這裏使用getMethodName()方法來給出異常棧軌跡所通過的方法名!ui
try {
...
} catch( Exception e ) {
throw e; // 從新拋出一個異常!
}
複製代碼
若只是簡單地將異常從新拋出,則然後用printStackTrace()顯示的將是原異常拋出點的調用棧信息,而非從新拋出點的信息,欲更正該信息,可使用fillInStackTrace()方法:spa
try {
...
} catch( Exception e ) {
throw (Exception)e.fillInStackTrace(); // 該行就成了異常的新發生地!
}
複製代碼
異常鏈:在捕獲一個異常後拋出另外一個異常,並但願將原始的異常信息保存下來!
解決辦法:
注意: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 );
}
}
複製代碼
try {
...
} catch(...) {
...
} finally { // finally子句老是會被執行!!!
...
}
複製代碼
使用時機:
try {
...
} catch(...) {
...
} finally { // finally子句老是會被執行!!!
sw.off(); // 最後老是須要關掉某個開關!
}
複製代碼
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!
複製代碼
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 ) {
...
}
}
}
複製代碼
上面的例子能夠總結以下:【注意對應數字標號】
對於在構造階段可能會拋出異常並要求清理的類,安全的方式是使用嵌套的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()
}
複製代碼