博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java编程思想学习录(连载之:异常)
阅读量:7233 次
发布时间:2019-06-29

本文共 8782 字,大约阅读时间需要 29 分钟。

Samsung Display


Thinking in java系列博文目录:

本篇文章将讲述关于异常的相关知识


基本概念

  • 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类型的异常代码}
  1. 虽然上面的id1和id2在处理异常代码中可能用不到,但不能少,必须定义
  2. 异常发生时,异常机制搜寻参数与异常类型相匹配的第一个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 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类型所持有的方法:

  • 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(); // 该行就成了异常的新发生地!}

异常链

异常链:在捕获一个异常后抛出另一个异常,并希望将原始的异常信息保存下来!

解决办法:

  1. 在异常的ctor中加入cause参数
  2. 使用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标准异常

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://tsvfm.baihongyu.com/

你可能感兴趣的文章
ubuntu设置分辨率
查看>>
助力春运 重庆机场今晨新增一架飞机入列
查看>>
助力国酒茅台智慧升级,京东地空巡检机器人启用
查看>>
支付宝第五代智能风控引擎AlphaRisk 保障钉钉数字化企业支付安全
查看>>
拥有荣耀手环4Running版,让你专业跑步科学健身!
查看>>
乐视手机销量将破2000万 有哪些手机厂商慌了?
查看>>
美美证券在美国上市 旗下拥有老牌券商Whitewood
查看>>
华为“ShapeCloud”DC集成解决方案 加速运营商数字化转型
查看>>
手机中的摄影战斗机 摄影圈大大带上它穿“越”啦
查看>>
精益求精,三星Galaxy C9 Pro令人惊艳
查看>>
山东启动非物质文化遗产月 助推乡村脱贫振兴
查看>>
西南民俗文化进高铁 春运旅客感受喜庆氛围
查看>>
极致品质创新科技 容声X7、容悦S8厅吧在京发布
查看>>
中国去年人民币贷款增16.17万亿元 房地产贷款增速回落
查看>>
港人首次置业项目逾80%申请者属“80后、90后”
查看>>
2018年中国外贸进出口总值30.51万亿元 贸易顺差收窄
查看>>
python|7月份GitHub上热门Python开源项目排行
查看>>
比特币跌了几天你就慌了吗?
查看>>
卖猪还钱 法院拍卖被执行人300头生猪 40.5万成交
查看>>
Python 高级编程:完全理解生成器
查看>>