throw抛出异常
异常是相对性的概念,对于不同的需求来说,异常不一定就是异常,所以我们不是对于每次判断的异常都要抛出,还有系统判断不出的异常,需要靠程序员抛出。
如果需要在程序中自行抛出异常,则应使用throw语句,throw语句可以单独使用,throw语句抛出的不是异常类,而是一个异常实例,而且每次只能抛出一一个异常实例。throw语法:throw ExceptionInstance;
系统自动抛出的异常和程序员抛出的异常在java运行时环境对异常处理是一样的。
如果throw语句抛出的异常是Checked异常,则该throw语句要么处于try块里,显式捕获该异常,要么放在一个带throws 声明抛出的方法中,即把该异常交给该方法的调用者处理;如果throw语句拋出的异常是Runtime异常,则该语句无须放在try块里,也无须放在带throws声明抛出的方法中;程序既可以显式使用try…catch 来捕获并处理该异常,也可以完全不理会该异常,把该异常交给该方法调用者处理。
自定义异常都应该继承Exception基类,如果希望自定义Runtime异常,则应该继承RuntimeException基类。定义异常类时通常需要提供两个构造器: 一个是无参数的构造器;另一个是带一个字符串参数的构造器,这个字符串将作为该异常对象的描述信息(也就是异常对象的getMessage()方法的返回值)。
前面介绍的异常处理方式有如下两种:
1.在出现异常的方法内捕获并处理异常,该方法的调用者将不能再次捕获该异常。
2.该方法签名中声明抛出该异常,将该异常完全交给方法调用者处理。
在实际应用中往往需要更复杂的处理方式——当一个 异常出现时,单靠某个方法无法完全处理该异常,必须由几个方法协作才可完全处理该异常。也就是说,在异常出现的当前方法中,程序只对异常进行部分处理,还有些处理需要在该方法的调用者中才能完成,所以应该再次抛出异常,让该方法的调用者也能捕获到异常。为了实现这种通过多个方法协作处理同一个异常的情形,可以在catch块中结合throw语句来完成。
从Java7开始,Java编译器会执行更细致的检查,Java编译器会检查throw语句抛出异常的实际类型。所以只要在throws方法签名中声明抛出对应类型异常就可以了。
在实际的完整程序中,是不会直接把异常传到用户界面。通常的做法是:程序先捕获原始异常,然后抛出一个新的业务异常,新的业务异常中包含了对用户的提示信息,这种处理方式被称为异常转译。
把捕获一个异常然后接着抛出另一个异常,并把原始异常信息保存下来是一种典型的链式处理(23种设计模式之一:职责链模式),也被称为“异常链"。
从JDK 1.4以后,所有Throwable的子类在构造器中都可以接收一cause对象作为参数。这个cause就用来表示原始异常,这样可以把原始异常传递给新的异常,使得即使在当前位置创建并抛出了新的异常,你也能通过这个异常链追踪到异常最初发生的位置。
java的异常跟踪栈
异常对象的printStackTrace()方法用于打印异常的跟踪栈信息,根据printStackTrace()方法的输出结果,开发者可以找到异常的源头,并跟踪到异常一路触发的过程。
面向对象的应用程序运行时,经常会发生一系列方法调用,从而形成“方法调用栈”,异常的传播则相反:只要异常没有被完全捕获(包括异常没有被捕获,或异常被处理后重新抛出了新异常),异常从发生异常的方法逐渐向外传播,首先传给该方法的调用者,该方法调用者再次传给其调用者…直至最后传到main 方法,如果main方法依然没有处理该异常,JVM会中止该程序,并打印异常的跟踪栈信息。
成功的异常处理应该实现如下4个目标:
1.使程序代码混乱最小化。
2.捕获并保留诊断信息。
3.通知合适的人员。
4.采用合适的方式结束异常活动。
基本准则:
1 不要过度使用异常
2 不要使用过于庞大的try块
3 避免使用Catch All语句
4 不要忽略捕获到的异常
try 块过于庞大时,就难免在try块后紧跟大量的catch块才可以针对不同的异常提供不同的处理逻辑。同一个try块后紧跟大量的catch块则需要分析它们之间的逻辑关系,反而增加了编程复杂度。正确的做法是,把大块的try块分割成多个可能出现异常的程序段落,并把它们放在单独的try块。
Catch All的两点不足:
1.所有的异常都采用相同的处理方式,这将导致无法对不同的异常分情况处理,如果要分情况处理,则需要在catch块中使用分支语句进行控制,这是得不偿失的做法。
2.这种捕获方式可能将程序中的错误、Runtime异常等可能导致程序终止的情况全部捕获到,从而“压制”了异常。如果出现了一些“关键”异常,那么此异常也会被“静悄悄”地忽略。
获取异常的后的三种措施:
1.处理异常。对异常进行合适的修复,然后绕过异常发生的地方继续执行;或者用别的数据进行计算,以代替期望的方法返回值;或者提示用户重新操作…总之,对于Checked异常,程序应该尽量修复。
2.重新抛出新异常。把当前运行环境下能做的事情尽量做完,然后进行异常转译,把异常包装成当前层的异常,重新抛出给.上层调用者。
3.在合适的层处理异常。如果当前层不清楚如何处理异常,就不要在当前层使用catch 语句来捕获该异常,直接使用throws声明抛出该异常,让上层调用者来负责处理该异常。