在Java(或任何其他带有受控异常的语言)中,当创建您自己的异常类时,您如何决定它应该被检查还是未检查?

我的直觉是,在调用者可能能够以某种有效的方式恢复的情况下,将调用checked异常,而作为未检查的异常则更多地用于不可恢复的情况,但我对其他人的想法感兴趣。


当前回答

检查例外: 如果客户端可以从异常中恢复,并希望继续,请使用受控异常。

未经检查的异常: 如果客户端在异常发生后不能做任何事情,则引发未检查的异常。

示例:如果您希望在方法a()中执行算术操作,并且基于a()的输出,则必须执行另一个操作。如果方法A()的输出为空,而您在运行时并不期望它,那么您将抛出空指针异常,即运行时异常。

请参考这里

其他回答

这不仅仅是从异常中恢复的能力问题。在我看来,最重要的是调用者是否对捕获异常感兴趣。

如果您编写的库用于其他地方,或应用程序中的较低级别层,请询问自己调用者是否有兴趣捕获(了解)您的异常。如果他不是,那么使用一个未检查的异常,这样就不会给他带来不必要的负担。

这是许多框架使用的哲学。尤其是Spring和hibernate——它们精确地将已知的受控异常转换为未检查异常,因为Java中过度使用了受控异常。我能想到的一个例子是来自json.org的JSONException,这是一个受检查的异常,最烦人的是——它应该是不受检查的,但开发人员根本没有考虑到这一点。

顺便说一下,大多数情况下,调用者对异常的兴趣与从异常中恢复的能力直接相关,但情况并非总是如此。

你是正确的。

未经检查的异常用于让系统快速失效,这是一件好事。为了正常工作,你应该清楚地说明你的方法期望什么。通过这种方式,您可以只验证输入一次。

例如:

/**
 * @params operation - The operation to execute.
 * @throws IllegalArgumentException if the operation is "exit"
 */
 public final void execute( String operation ) {
     if( "exit".equals(operation)){
          throw new IllegalArgumentException("I told you not to...");
     }
     this.operation = operation; 
     .....  
 }
 private void secretCode(){
      // we perform the operation.
      // at this point the opreation was validated already.
      // so we don't worry that operation is "exit"
      .....  
 }

举个例子。关键是,如果系统快速失效,那么你就会知道它在哪里以及为什么会失效。你会得到这样的堆栈跟踪:

 IllegalArgumentException: I told you not to use "exit" 
 at some.package.AClass.execute(Aclass.java:5)
 at otherPackage.Otherlass.delegateTheWork(OtherClass.java:4569)
 ar ......

你会知道发生了什么。“delegateTheWork”方法中的OtherClass(在第4569行)使用“exit”值调用你的类,即使它不应该这样做等等。

否则你将不得不在你的代码中散布验证,这很容易出错。另外,有时很难跟踪哪里出了问题,可能会有几个小时令人沮丧的调试

同样的事情也发生在nullpointerexception中。如果你有一个700行的类,有15个方法,使用30个属性,它们都不能为空,而不是在每个方法中验证可为空性,你可以使所有这些属性都是只读的,并在构造函数或工厂方法中验证它们。

 public static MyClass createInstane( Object data1, Object data2 /* etc */ ){ 
      if( data1 == null ){ throw NullPointerException( "data1 cannot be null"); }

  }


  // the rest of the methods don't validate data1 anymore.
  public void method1(){ // don't worry, nothing is null 
      ....
  }
  public void method2(){ // don't worry, nothing is null 
      ....
  }
  public void method3(){ // don't worry, nothing is null 
      ....
  }

当程序员(你或你的同事)做的每件事都是正确的,验证输入,运行测试,所有的代码都是完美的,但代码连接到第三方web服务可能会关闭(或你正在使用的文件被另一个外部进程删除等)时,检查异常是有用的。web服务甚至可以在连接尝试之前进行验证,但是在数据传输过程中出错了。

在这种情况下,你或你的同事都无能为力。但你还是得做点什么,不能让应用程序在用户眼中消失。你使用一个检查异常来处理异常,当发生这种情况时你能做什么?,大多数时候,只是尝试记录错误,可能会保存你的工作(应用程序工作),并向用户显示消息。(网站blabla宕机,请稍后重试)

如果检查过的异常被过度使用(通过在所有方法签名中添加“throw exception”),那么您的代码将变得非常脆弱,因为每个人都会忽略这个异常(因为太普遍),代码质量将严重受损。

如果过度使用未检查异常,也会发生类似的情况。这段代码的用户不知道是否会出现错误,因此进行了大量的尝试{…}catch(Throwable t)将出现。

检查例外: 如果客户端可以从异常中恢复,并希望继续,请使用受控异常。

未经检查的异常: 如果客户端在异常发生后不能做任何事情,则引发未检查的异常。

示例:如果您希望在方法a()中执行算术操作,并且基于a()的输出,则必须执行另一个操作。如果方法A()的输出为空,而您在运行时并不期望它,那么您将抛出空指针异常,即运行时异常。

请参考这里

当不太可能出现异常时,即使在捕捉到异常之后,我们也可以继续,并且我们不能做任何事情来避免该异常,那么我们可以使用受控异常。

当我们想做一些有意义的事情时,当一个特定的异常发生时,当这个异常是预期的,但不是确定的,那么我们可以使用受控异常。

当异常在不同的层中导航时,我们不需要在每一层都捕获它,在这种情况下,我们可以使用运行时异常或包装异常作为未检查的异常。

运行时异常是在异常最有可能发生的情况下使用的,没有办法进一步进行,并且没有任何东西可以恢复。在这种情况下,我们可以对这种异常采取预防措施。EX: NUllPointerException, ArrayOutofBoundsException。这些更有可能发生。在这种情况下,我们可以在编码时采取预防措施来避免这种异常。否则,我们将不得不在每个地方都写入try catch块。

更一般的例外情况可以设置为Unchecked,不太一般的例外情况将被检查。

我们必须根据是否是程序员错误来区分这两种类型的异常。

If an error is a programmer error, it must be an Unchecked Exception. For example: SQLException/IOException/NullPointerException. These exceptions are programming errors. They should be handled by programmer. While in JDBC API, SQLException is Checked Exception, In Spring JDBCTemplate it is an Unchecked Exception.Programmer doesn't worry about SqlException, when use Spring. If an error is not a programmer error and the reason is coming from external, it must be a Checked Exception. For example: if the file is deleted or file permission is changed by someone else, It should be recovered.

FileNotFoundException是理解细微差别的好例子。在找不到文件的情况下抛出FileNotFoundException。这种例外有两个原因。如果文件路径是由开发人员定义的或通过GUI从最终用户获取的,那么它应该是一个未检查的异常。如果文件被其他人删除,它应该是一个Checked Exception。

检查异常可以用两种方式处理。它们使用try-catch或传播异常。在异常传播的情况下,由于异常处理,调用堆栈中的所有方法都将紧密耦合。这就是为什么我们必须小心地使用已检查异常。

如果您开发了一个分层的企业系统,您必须选择大多数未检查的异常来抛出,但不要忘记在您什么都做不了的情况下使用已检查的异常。