基本概念

  • Java使用异常来提供一致性的错误报告模型;且可集中错误处理;且任务代码与异常代码分割开来,易于理解和维护
  • 虽然异常处理理论有终止模型恢复模型两种,但恢复模型很难优雅地做到,∴并不实用,实际中大家都是转向使用终止模型代码
  • 一个异常抛出后发生的两件事:① 使用new在堆上创建异常对象;② 异常处理机制开始接管流程(当前的执行流程被终止)
  • 标准异常类均有两个ctor:① default ctor; ② 带字符串参数的ctor
  • Throwable是异常类型的根类
  • catch异常时,try中抛出的是子类异常,但catch的是基类异常也是OK,但若catch子类异常和基类异常的子句
  • 同时存在时,应将基类catch子句放在后面避免“屏蔽”现象发生

抛出异常 + 捕获异常

  • 抛出异常(throw):
if( t==null )throw new NullPointerException(); // 异常对象用new创建于堆上
12

  • 捕获异常(try+catch):
try {...
} catch( Type1 id1 ) { // 处理Type1类型的异常代码
} catch( Type2 id2 ) {// 处理Type2类型的异常代码
}
1234567

  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 fun
Caught it !
123456789101112131415161718192021222324

创建带参数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()
MyExceptionat ...at ...
Throwing MyException from g()
MyException: Originated in g() // 此即创建异常类型时传入的String参数at ...at ...
123456789101112131415161718192021222324252627282930313233343536373839404142434445


捕获所有异常

try {...
} catch( Exception e ) { // 填写异常的基类,该catch子句一般置于末尾...
}
12345

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() );
}
123456

这里使用getMethodName()方法来给出异常栈轨迹所经过的方法名!


重抛异常

try {...
} catch( Exception e ) {throw e;   // 重新抛出一个异常!
}
12345

若只是简单地将异常重新抛出,则而后用printStackTrace()显示的将是原异常抛出点的调用栈信息,而非重新抛出点的信息,欲更正该信息,可以使用fillInStackTrace()方法:

try {...
} catch( Exception e ) {throw (Exception)e.fillInStackTrace(); // 该行就成了异常的新发生地!
}
12345


异常链

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

解决办法: 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 );}}
123456789101112131415161718


Java标准异常

  • 看这个图需要明确:程序员一般关心Exception基类型的异常
  • 由图中可知,Error、RuntimeException都叫做“Unchecked Exception”,即不检查异常,程序员也无需写异常处理的代码,这种自动捕获
  • 若诸如RuntimeException这种Unchecked异常没有被捕获而直达main(),则程序在退出前将自动调用异常的printStackTrace()方法

使用finally进行清理

try {...
} catch(...) {...
} finally { // finally子句总是会被执行!!!...
}
1234567

使用时机: - 当需要把内存之外的资源(如:文件句柄、网络连接、某个外部世界的开关)恢复到初始状态时!

try {...
} catch(...) {...
} finally { // finally子句总是会被执行!!!sw.off(); // 最后总是需要关掉某个开关!
}
1234567

  • 在return中使用finally
public static void func( int i ) {try {if( i==1 )return;if( i==2 )return;} finally {print( "Performing cleanup!" ); // 即使上面有很多return,但该句肯定被执行}}
123456789101112

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!
123456789101112131415161718192021222324252627282930313233343536373839

  • finally子句中的return
public static void main( String[] args ) {try {throw new RuntimeException();} finally {return; // 这将会掩盖所有的异常抛出}
}
1234567


继承基类、实现接口时的异常限制

// 异常类
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 ) {...}}
}1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162

上面的例子可以总结如下:【注意对应数字标号】 - (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()
}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869

throw new exception后程序不停止_Java之Exception剖析相关推荐

  1. Edge浏览器打开控制台后程序总是停止进入debug模式关闭教程【八仙过海之又一过海方案】

    问题描述 就是这样,我打开控制台想看接口调用过程,但是Edge总是给我暂停,不知道是版本还是什么问题,以前也没有出现,看了网上的教程,可谓:八仙过海,各显神通!我试了,都不好使 解决办法 如果你也是看 ...

  2. java程序课程总结_java课程总结

    课程总结 一个学期结束了,下面我对一些重点知识分三个模块做一个小的总结. 一.Java基础程序设计 1.java中源文件的扩展名为.java,之后通过编译是.java的文件生成一个.class文件. ...

  3. android已停止三星,急,android离线打包程序三星下总报 程序 已停止

    解开锁屏后,就看到 图片所示的 程序 已停止的提示框,logcat显示如下 11-27 15:33:33.725: I/AndroidRuntime(24861): VM exiting with r ...

  4. java自定义事件案例_Java Custom Exception Example(Java自定义异常案例)

    In this example we will look briefly(短暂的) at the basics of Exception, in Java Programming Language. ...

  5. java程序启动打印_JAVA打印问题:程序正常运行,打印机无反应

    我需要打印一份TXT文件,但是运行后程序未出现异常,打印机却没有反应.打印机在其他地方都是正常工作的.求解为什么???打印机型号:GprinterGP-9035T代码:importjava.awt.G ...

  6. 应用程序发生异常unknown software exception的解决方法

    原文:http://www.veryhuo.com/a/vie 应用程序发生异常 unknown software exception(0xc0000096),位置为0x02fe6818 在控制面板的 ...

  7. stm32按下复位键后程序停止运行,重新上电又可以运行

    文章目录 问题描述 一.问题排查 二.问题原因 1.boot引脚没有地或者VCC 总结 问题描述 今天遇到了一个比较有意思的问题,大致就是在做蓝牙串口通信时,发现自己焊接的板子出现了按下reset键时 ...

  8. 计算机最基础软件怎么应用程序,如何解决Win7系统运行软件就会提示Microsoft基础类应用程序已停止工作-电脑自学网...

    有Win7系统用户反馈说每次打开软件时就会提示Microsoft基础类应用程序已停止工作,并且无法运行软件,这是怎么回事呢?为什么Win7系统运行软件就会提示Microsoft基础类应用程序已停止工作 ...

  9. 95后程序员月薪2万背着电脑送外卖,送单途中改Bug

    公众号关注 "视学算法" 设为 "星标",消息即可送达! 来源:云技术(ID:aiyunjishu) [#95后程序员背着电脑送外卖# 随时应对甲方临时需求] ...

  10. 95后程序员业余帮人鉴定毒蘑菇,竟成百万粉丝的网络大V!

    许多人都在主业之外发展副业,程序员也不例外,有人独立开发app,有人业余炒股,有人下班后写写技术类公众号,这些都是比较常见的副业. 但在南京有这样一个95后程序员小哥哥,在工作之外,他还是一个冷门领域 ...

最新文章

  1. OTA常见方案分析(差分升级 全量升级 AB面升级 Recovery系统升级)
  2. Python easyGUI 文件浏览 显示文件内容
  3. win7下一次加载和调试sys驱动程序的过程以及捕捉到内核打印字符串函数的输出
  4. CBOX直播故障:正在连接
  5. storm kafkaSpout 踩坑问题记录! offset问题!
  6. 二进制八进制十六进制之间的快速转换------ 心算笔算方法总结
  7. CF628D Magic Numbers (数据大+数位dp)求[a,b]中,偶数位的数字都是d,其余为数字都不是d,且能被m整除的数的个数...
  8. 计算机的硬件简介,计算机基础之硬件简介(Day2)(示例代码)
  9. windows下python安装
  10. 手把手教学系列——疯狂Spring Cloud教学视频
  11. MAC VSCode Go代码第一次运行配置
  12. tensorflow sigmoid 如何计算训练数据的正确率_初探 TensorFlow.js
  13. 计算机组成原理课设模板,计算机组成原理课程设计报告模板(2011).doc
  14. 编写时间的php,PHP如何实现简单日历类编写 PHP实现简单日历类编写代码
  15. c 打印二叉树_基础扩展 | 22. 遍历二叉树—前序遍历算法的VBA代码解析
  16. Linux设置Oracle环境变量
  17. QT Designer的安装和使用
  18. word论文公式加点
  19. 用VMware克隆CentOS 6.4后HWaddr和UUID的设置
  20. strtolower() 把字符串转换为小写字母

热门文章

  1. 19. 用 GTK+ 进行GNOME 编程,用 Qt进行KDE 编程
  2. 12.第二节 SAPI概述
  3. java JSONObject JSONArray对象使用小记
  4. 一个奇怪的发现:html与body,body的margin对html不起作用,html的padding对body却起作用
  5. msysgit+apache安装说明 - gitweb服务器部分
  6. 微信公众平台消息接口开发(26)从Hello2BizUser文本到subscribe事件
  7. vs2010下libevent的使用
  8. SharePoint2010企业开发最佳实践(八)---- SPWeb 对象
  9. WinForm模拟单击按钮两种方法
  10. 查找算法之六 哈希查找(C++版本)