学习博文 如何正确使用Java异常处理机制

来源:互联网 发布:网络连接中断 编辑:程序博客网 时间:2024/04/23 23:36

https://www.cnblogs.com/hihtml5/p/6505994.html

本文主要是基于以上博文做的一个总结。


一、异常发生的原因


如图1所示,你写的方法和外部实体交互大概可以分为五类:

1和资源(Resource)交互,见图⑤处。这里资源的范围很广,比如进程外部的数据库,文件,SOA服务,其他各种中间件;进程内的类,方法,线程……都算是资源。

2给进程内的其他方法(User Method)提供服务,见图②处。

3依赖进程内的其他方法(Server Method),见图③处。包括Java平台提供的方法和其他第三方供应方提供的方法。

4和系统环境交互,见图⑧处。系统环境可能是直接环境——JVM,也可能是间接环境——操作系统或硬件等。

5给外部实体提供服务,见图①处。这种外部实体一般会通过容器(或其他类似的机制)和你的方法进行交互。所以,可以归为②,不予探讨。

pastedGraphic.png


1:你的方法与其他实体交互概要图


Java方法和每一类实体进行交互时,都可能发生异常。

  1. 当和资源交互时,常常会因为资源不可用而发生异常,比如发生找不到文件、数据库连接错误、找不到类、找不到方法……等等状况。对于这类异常,通常有以下几个特点:
    • 问题来自外部,你的方法本身的正常流程逻辑没问题。
    • 这类异常通常是暂时的,过段时间就可用了(经过资源维护者的彻夜奋战……)。最终用户通常也可以接受暂时的等待或采取替补方案。
    • 你的程序的其他功能还可以用。

           这时,你的方法应该这样处理:

    • 返回到一种安全状态,并能够让用户执行一些其他的命令(比如支付宝支付失败时返回一个弹出框,说明余额不足等原因,让用户重新选择其他支付渠道);
    • 或者允许用户保存所有操作的结果,并以适当的方式终止程序(比如保存填了一半的表单)。

         然后,你应该协调各方,促进资源恢复可用,消除异常。

  1. 当给用户方法(User Method提供服务时,用户可能会传入一些不合法的数据(或者其他不恰当的使用方法),进而对程序的正常流程造成破坏。你的方法应该检查每一个输入数据,如果发现不合法的数据,马上阻止执行流程,并通知用户方法。
  2. 当调用服务方法(Server Method时,有可能会发生两类异常。一类是你的使用方法不正确,导致服务中止;一类是服务方法出了异常,然后传递给你的方法。如果是第一种异常,你应该检查并修改你的方法逻辑,消除BUG。对于第二类异常,你要么写一个处理器处理,要么继续传递给上层方法。
  3. 当和系统环境交互时,有可能因为JVM参数设置不当,有可能因为程序产生了大量不必要的对象,也有可能因为硬故障(操作系统或硬件出了问题),导致整个程序不可用。当这类异常发生时,最终用户没法选择其他替代方案,操作到一半的数据会全部丢失。你的方法对这类异常一般没什么办法,既不能通过修改主流程逻辑来消除,也不能通过增加异常处理器来处理。所以通常你的方法对这类异常不需要做任何处理。但是你必须检查进程内的所有程序和系统环境是否正常,然后协调各方,修改BUG或恢复环境

Java的异常都是发生在方法内,所以研究Java异常,要以你设计的方法为中心。我们以你的方法为中心,总结一下内部处理办法:

当服务方法告诉你的方法主流程逻辑有问题时,就要及时修复BUG来消除异常;

用户方法非法使用你的方法时,应该直接中止主流程,并通知用户方法,强迫用户方法使用正确的方式,防止问题蔓延;

服务方法传递一个异常给你的方法时,你要判断你的方法是否合适处理这个异常,如果不合适,传递给上层方法,如果合适,写一个异常处理器处理这个异常。

当系统环境出了问题,你的方法什么也做不了。

现在以你的方法为中心,总结一下方法外部的处理方法:

当资源不可用的时候,你应该协调各方,恢复资源;

当发生系统故障时,你应该协调各方,恢复系统。


二、JAVA异常处理类


     Java把异常当做是破坏正常流程的一个事件,当事件发生后,就会触发处理机制。

     Java有一套独立的异常处理机制,在遇到异常时,方法并不返回任何值(返回值属于正常流程),而是抛出一个封装了错误信息的对象。下图是Java异常处理机制类层级结构图:


2Java异常处理机制类层级结构图



      所有派生于Throwable类的异常类,基本都没有这些成员方法,也就是说所有的异常类都只是一个标记,记录发生了什么类型的异常(通过标记,编译期和JVM做不同的处理),所有实质性的行为Throwable都具备了。

      综上,在一个Throwable里面可以获取什么信息?

  • 获取堆栈跟踪信息(源代码中哪个类,哪个方法,第几行出现了问题……从当前代码到最底层的代码调用链都可以查出来)
  • 获取引发当前ThrowableThrowable。追踪获取底层的异常信息。
  • 获取被压抑了,没抛出来的其他Throwable。一次只能抛出一个异常,如果发生了多个异常,其他异常就不会被抛出,这时可以通过加入suppressed异常列表来解决(JDK7以后才有)。
  • 获取基本的详细描述信息

     从图2可以看出,Throwable类只有两个直接继承者:ErrorException。然后Exception又分为RuntimeExceptionChecked Exception


2.2 Error

      Java中,由系统环境问题引起的异常,一般都继承于Error类。

对于Error类:

     一般开发者不要自定义Error子类,因为它代表系统级别的错误。与一般的程序无关。

     Java异常处理机制中,Error不强制捕获或声明,也就是不强制处理。因为程序本身对此类错误无能为力。一般情况下我们只要把堆栈跟踪信息记录下来就行。

    下列是Java平台中直接继承于Error的错误类型:

AnnotationFormatErrorAssertionErrorAWTErrorCoderMalfunctionErrorFactoryConfigurationError,FactoryConfigurationErrorIOErrorLinkageErrorServiceConfigurationErrorThreadDeath,TransformerFactoryConfigurationErrorVirtualMachineError


2.3 Exception


     Java中,除了系统环境问题引起的异常,一般都继承于Exception Exception分为RuntimeExceptionChecked ExceptionChecked Exception必须要捕获或声明。而RuntimeException不强制。

对于Exception类:如果你创建了一个异常类型,直接继承于Exception,那么这个异常类型将属于检查异常(Checked Exception)。

2.3.1  RuntimeException

Java中,由于接口方法使用不当造成的异常,一般属于RuntimeException,也就是运行时异常。

对于RuntimeException

如果你调用服务方法的方式不正确,你应该马上修改代码,避免发生RuntimeException

如果是用户方法调用你的方法的方式不正确,你应该立刻抛出RuntimeException,强制让使用者修正代码或改变使用方式,防止问题蔓延

一般情况下,不要捕获或声明RuntimeException。因为问题在于你的程序本身有问题,如果你用异常流程处理了,反而让正常流程问题一直存在

下列是Java平台中直接继承于RuntimeException的运行时异常:

AnnotationTypeMismatchExceptionArithmeticExceptionArrayStoreExceptionBufferOverflowException,BufferUnderflowExceptionCannotRedoExceptionCannotUndoExceptionClassCastException,CMMExceptionConcurrentModificationException,DataBindingExceptionDOMException,EmptyStackExceptionEnumConstantNotPresentExceptionEventException,FileSystemAlreadyExistsExceptionFileSystemNotFoundExceptionIllegalArgumentException,IllegalMonitorStateException,IllegalPathStateExceptionIllegalStateExceptionIllformedLocaleException,ImagingOpException,IncompleteAnnotationExceptionIndexOutOfBoundsException,JMRuntimeExceptionLSExceptionMalformedParameterizedTypeException,MirroredTypesException,MissingResourceExceptionNegativeArraySizeException,NoSuchElementExceptionNoSuchMechanismExceptionNullPointerExceptionProfileDataException,ProviderExceptionProviderNotFoundExceptionRasterFormatExceptionRejectedExecutionException,SecurityExceptionSystemExceptionTypeConstraintExceptionTypeNotPresentException,UndeclaredThrowableExceptionUnknownEntityExceptionUnmodifiableSetException,UnsupportedOperationExceptionWebServiceExceptionWrongMethodTypeException

2.3.2 Checked Exception

Java中,直接或间接因为资源问题引起的异常,一般属于检查异常(Checked Exception。检查异常继承于Exception,而不继承于RuntimeException

对于检查异常:

必须捕获或声明

交给关心这个异常的方法处理

异常处理器应该引导用户接下来怎么办,至少做到安全退出

下列是Java平台中直接继承于Exception的检查异常:

AclNotFoundExceptionActivationExceptionAlreadyBoundExceptionApplicationExceptionAWTException,BackingStoreExceptionBadAttributeValueExpExceptionBadBinaryOpValueExpException,BadLocationExceptionBadStringOperationException,BrokenBarrierExceptionCertificateException,CloneNotSupportedExceptionDataFormatExceptionDatatypeConfigurationExceptionDestroyFailedException,ExecutionExceptionExpandVetoExceptionFontFormatExceptionGeneralSecurityException,GSSException,IllegalClassFormatExceptionInterruptedExceptionIntrospectionExceptionInvalidApplicationException,InvalidMidiDataExceptionInvalidPreferencesFormatExceptionInvalidTargetObjectTypeException,IOExceptionJAXBExceptionJMException,KeySelectorExceptionLastOwnerException,LineUnavailableExceptionMarshalExceptionMidiUnavailableExceptionMimeTypeParseException,MimeTypeParseExceptionNamingExceptionNoninvertibleTransformExceptionNotBoundException,NotOwnerExceptionParseExceptionParserConfigurationExceptionPrinterExceptionPrintException,PrivilegedActionExceptionPropertyVetoExceptionReflectiveOperationExceptionRefreshFailedException,RemarshalExceptionSAXException,ScriptExceptionServerNotActiveExceptionSOAPException,SQLExceptionTimeoutExceptionTooManyListenersExceptionTransformerExceptionTransformException,UnmodifiableClassExceptionUnsupportedAudioFileExceptionUnsupportedCallbackException,UnsupportedFlavorExceptionUnsupportedLookAndFeelExceptionURIReferenceExceptionURISyntaxExceptionUserExceptionXAExceptionXMLParseExceptionXMLSignatureException,XMLStreamExceptionXPathException

2.3.3 Uncheck Exception

     ErrorRuntimeException统称为非检查异常。两者的共同点就是都不被强制捕获或声明。实际上两者描述问题的范围完全没有交集。

2.4 总结

      所有的功能都在Throwable类里面实现了,子类只需要直接继承或间接继承它,并且加上需要的构造方法就行(一般而言,第一第二个构造方法是必须的,也可以全部加上),而且构造方法通常只需要一行代码:super(...),也就是说只要调用父类的构造方法就行了。

      Java把异常分为三类(ErrorChecked ExceptionRuntimeException),只是在语法层面上有不同的标记而已。它们自身拥有的功能一样,运行时系统处理它们的方式也是一样的(你也可以捕获或声明非检查异常),不同的是编译器对它们的区别对待(检查异常必须要在代码里处理,非检查异常就不需要),以及程序员对它们的区别对待(这需要程序员遵循良好的实践原则)。

     这三类异常全部覆盖了第一节中所描述的异常发生场景,图1中,④⑤⑥处可能会发生Checked Exception,②③处既可能会发生RuntimeException也可能会发生Checked Exception,⑦⑧⑨处可能会发生Error。①处已经超出了Java异常处理机制的范畴(这属于容器要考虑的问题),通常在数据中加入返回码来通知异常信息。







原创粉丝点击