本文共 8782 字,大约阅读时间需要 29 分钟。
Thinking in java系列博文目录:
本篇文章将讲述关于异常的相关知识
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 funCaught 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类型所持有的方法:
注意:从下往上每个方法都比前一个提供了更多的异常信息!
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(); // 该行就成了异常的新发生地!}
异常链:在捕获一个异常后抛出另一个异常,并希望将原始的异常信息保存下来!
解决办法:
注意: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()}
转载地址:http://tsvfm.baihongyu.com/