Fullstar

Archives

  • December 2025
  • August 2024
  • July 2024
  • February 2024
  • November 2023
  • August 2023
  • July 2023
  • January 2023
  • November 2022
  • October 2022
  • September 2022
  • February 2022
  • January 2022
  • September 2021
  • January 2021
  • December 2020
  • November 2020
  • October 2020
  • September 2020
  • August 2020
  • July 2020

Categories

  • Code
  • Lens
  • Life
0
Fullstar

Posts by month

February 2022

1 post
  • Code

异常处理的合理使用与防御式编程

  • February 21, 2022
  • Brandon
一.JAVA中的异常处理机制 在Java中异常定义为Throwable类,其有两个子类分别为Error类与Expection类。其中Error由JVM抛出,用于描述系统内部错误或资源耗尽错误,这种错误一旦发生无法由程序进行处理,仅能通知用户并终止程序。Exception则指的是程序能够进行处理的异常,其中RuntimeException类异常指由于程序错误导致的异常,如数组越界等。 在Java中异常处理机制分为抛出异常与捕捉异常,抛出异常将异常对象层层抛出直至JVM,而捕捉异常则使用try,catch,finally,throw,throws关键字进行异常捕捉。其中catch异常捕获存在通用原则:越是底层的异常越应定义在后部;finally常用于关闭外界资源,如实体管理器工厂、实体管理器、数据库连接等,且若在try,catch语句块内遇到return语句则finally语句块将在return之前执行,但finally语句块在下列情况下将不会被执行:1.在finally语句块中发生了异常2.提前使用exit()函数退出程序3.程序所在线程死亡。 且若现存异常不满足需求则在Java中可通过继承Exception自定义异常类。 二.SpringBoot中的异常处理 SpringBoot中的异常处理分为全局异常捕获处理与局部异常捕获处理。在SpringBoot项目开发过程中可以发现用户输入错误数据导致程序出现异常程序不会终止,而是显示Whitelabel Error Page页面并显示错误提示,这是由于SpringBoot提供了一套默认的异常处理机制,一旦程序出现错误就会自动请求/error,并将其交由BasicExceptionController处理,由其返回默认显示页面并显示异常信息。 而若需要对异常处理方式进行自定义,针对局部异常处理可使用@ExceptoinHandler注解,如@ExceptionHandler(value={ArithmeticException.class}), 则当在此controller内出现ArithmeticException异常时将调用此方法进行异常处理。针对全局异常处理则可将@ControllerAdvice与@ExceptionHandler配合使用,将@ControllerAdvice加之类前从而将此类定义为全局异常处理类,再在该类的方法前使用@ExceptionHandler注解则能够对特定异常执行特定处理方法。 三.异常的合理使用 把异常当做正常处理逻辑的一部分的那种程序,就会遭受与所有典型的意大利面条式代码同样的可读性和可维护性问题。 Andy Hunt 和 Dave Thomas 上述的句子引自《代码大全第二版》,个人理解:异常处理应只能完成其本职工作,即异常处理本身,而不应该将程序正常运行的逻辑放入异常处理之中,否则随着异常处理代码逻辑越来越繁杂,其本身将可能像正常代码逻辑一样产生异常。且异常处理相比于正常逻辑处理将需要更多开销,在框架中由其如此,如在SpringBoot项目中,假设需要接收用户注册所输入的用户名信息,由于用户很可能输入不规范字符类型,因此需要对输入进行检测,在这种情况下若利用异常处理机制能够很方便地进行处理且不影响正常输入代码逻辑,但SpringBoot框架在进行异常处理中需要调用框架所集成的异常处理机制,若开启日志则花销将更加巨大,因此对于类似业务逻辑可以使用原始的输入检测方法代替异常处理。 同时在《代码大全第二版》中给出如下的异常处理建议: 建议一是基于异常本身设计的用意,即提供无法被忽略的错误通知机制,从而将错误控制在一定的范围内避免扩散。 建议二是为了防止异常的滥用,使用异常将使得程序复杂性增加,且异常向上抛出的过程中调用函数需要了解被调用函数可能抛出的异常,这将弱化封装性。 建议三指明异常捕获的位置不可一再拖延,若当前代码块能够处理异常就应当在当前代码块内处理异常。 建议四是基于构造函数与析构函数自身的复杂性,当将异常引入构造函数与析构函数时情况将变得极其复杂,因此个人选择在构造函数与析构函数中不使用异常处理。 建议五是为了防止对代码封装性的破坏。对象与对象之间存在层间关系,上下层之间的抽象程度不同,从原则上讲上层代码不应了解下层代码的实现细节,而异常的抛出将会暴露低层的实现方式,因此不同层间抛出的异常抽象程度也应不同。如底层函数为文件处理,因此可能返回EOFException异常,若将此异常返回至调用方将暴露自身的实现细节,因此应将低层异常封装为自定义异常对象,当产生异常时返回此自定义异常,从而避免封装性的破坏。 建议六是为了使异常抛出的价值最大化。当异常抛出时说明程序某处发生了异常错误,此时应当将错误产生的原因详细输出,使得用户可以迅速定位问题发生处,因此许多框架使用日志对异常进行记录。 建议七中假设使用了空的catch语句,则表示当异常发生时不进行任何处理,则用户无法判断异常是否发生,而这种情况下产生的潜在危害甚至大于不进行异常捕获,不进行异常捕获至少当异常发生时用户能够注意到异常从而进行相应的处理。同时在书中提到若确实存在某种情况必须采用空的catch语句则需要使用注释或向日志文件中记录信息从而将此情况“文档化”。 建议八主要针对不要求子程序定义可能抛出的异常的编程语言。若对函数库可能抛出的异常不了解则当函数库抛出异常时将导致程序崩溃。 建议九中所提及的集中异常报告机制类似于SpringBoot中的全局异常处理类。通过将异常处理集中于一个类中从而确保异常处理的一致性。 建议十即要求一个项目在构建前需要对异常的使用进行标准化限制,如规定何种场合允许使用throw-catch语句,何种场合允许将异常抛出,是否允许在构造函数与析构函数中使用异常等。由于异常在通常情况下可能散布于程序的各个模块内,若不对异常进行标准化限制将导致项目变得更加杂乱。 建议十一指出不要为了使用异常处理机制而使用异常,在许多情况下可以采用更好的方式来替代异常处理,在使用异常处理前应像考虑选择哪种算法一样考虑异常处理机制在当前场景下是否优于其他错误处理机制。…
View Post
Share
Fullstar

Input your search keywords and press Enter.