Java 何时选择已检查和未检查的异常

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27578/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 07:19:42  来源:igfitidea点击:

When to choose checked and unchecked exceptions

javaexceptionchecked-exceptions

提问by Matt Sheppard

In Java (or any other language with checked exceptions), when creating your own exception class, how do you decide whether it should be checked or unchecked?

在 Java(或任何其他带有检查异常的语言)中,在创建自己的异常类时,您如何决定是否应该检查或取消检查?

My instinct is to say that a checked exception would be called for in cases where the caller might be able to recover in some productive way, where as an unchecked exception would be more for unrecoverable cases, but I'd be interested in other's thoughts.

我的直觉是说在调用者可能能够以某种富有成效的方式恢复的情况下会调用受检查的异常,而未经检查的异常更适用于不可恢复的情况,但我会对其他人的想法感兴趣。

采纳答案by Gili

Checked Exceptions are great, so long as you understand when they should be used. The Java core API fails to follow these rules for SQLException (and sometimes for IOException) which is why they are so terrible.

Checked Exceptions 很棒,只要您了解何时应该使用它们。Java 核心 API 未能遵循 SQLException(有时是 IOException)的这些规则,这就是它们如此糟糕的原因。

Checked Exceptionsshould be used for predictable, but unpreventableerrors that are reasonable to recover from.

Checked Exceptions应该用于可预测不可预防的错误,这些错误可以合理地从中恢复

Unchecked Exceptionsshould be used for everything else.

未经检查的异常应该用于其他一切。

I'll break this down for you, because most people misunderstand what this means.

我会为你分解这个,因为大多数人都误解了这意味着什么。

  1. Predictable but unpreventable: The caller did everything within their power to validate the input parameters, but some condition outside their control has caused the operation to fail. For example, you try reading a file but someone deletes it between the time you check if it exists and the time the read operation begins. By declaring a checked exception, you are telling the caller to anticipate this failure.
  2. Reasonable to recover from: There is no point telling callers to anticipate exceptions that they cannot recover from. If a user attempts to read from an non-existing file, the caller can prompt them for a new filename. On the other hand, if the method fails due to a programming bug (invalid method arguments or buggy method implementation) there is nothing the application can do to fix the problem in mid-execution. The best it can do is log the problem and wait for the developer to fix it at a later time.
  1. 可预测但不可预防:调用者尽其所能验证输入参数,但某些超出其控制范围的情况导致操作失败。例如,您尝试读取一个文件,但在您检查文件是否存在和读取操作开始之间,有人将其删除。通过声明一个检查异常,你告诉调用者预测这个失败。
  2. 合理的恢复:告诉调用者预测他们无法恢复的异常是没有意义的。如果用户试图读取一个不存在的文件,调用者可以提示他们输入一个新的文件名。另一方面,如果方法由于编程错误(无效的方法参数或错误的方法实现)而失败,则应用程序无法在执行中修复问题。它可以做的最好的事情是记录问题并等待开发人员稍后修复它。

Unless the exception you are throwing meets allof the above conditions it should use an Unchecked Exception.

除非您抛出的异常满足上述所有条件,否则它应该使用 Unchecked Exception。

Reevaluate at every level: Sometimes the method catching the checked exception isn't the right place to handle the error. In that case, consider what is reasonable for your own callers. If the exception is predictable, unpreventable and reasonable for them to recover from then you should throw a checked exception yourself. If not, you should wrap the exception in an unchecked exception. If you follow this rule you will find yourself converting checked exceptions to unchecked exceptions and vice versa depending on what layer you are in.

在每个级别重新评估:有时捕获已检查异常的方法不是处理错误的正确位置。在这种情况下,请考虑对您自己的来电者来说什么是合理的。如果异常是可预测的、不可预防的并且可以合理地让他们从中恢复,那么您应该自己抛出一个已检查的异常。如果没有,您应该将异常包装在未经检查的异常中。如果您遵循此规则,您会发现自己将检查异常转换为未检查异常,反之亦然,具体取决于您所在的层。

For both checked and unchecked exceptions, use the right abstraction level. For example, a code repository with two different implementations (database and filesystem) should avoid exposing implementation-specific details by throwing SQLExceptionor IOException. Instead, it should wrap the exception in an abstraction that spans all implementations (e.g. RepositoryException).

对于已检查和未检查的异常,请使用正确的抽象级别。例如,具有两种不同实现(数据库和文件系统)的代码存储库应避免通过抛出SQLException或 来暴露特定于实现的细节IOException。相反,它应该将异常包装在一个跨越所有实现的抽象中(例如RepositoryException)。

回答by Espo

From A Java Learner:

来自Java 学习者

When an exception occurs, you have to either catch and handle the exception, or tell compiler that you can't handle it by declaring that your method throws that exception, then the code that uses your method will have to handle that exception (even it also may choose to declare that it throws the exception if it can't handle it).

Compiler will check that we have done one of the two things (catch, or declare). So these are called Checked exceptions. But Errors, and Runtime Exceptions are not checked for by compiler (even though you can choose to catch, or declare, it is not required). So, these two are called Unchecked exceptions.

Errors are used to represent those conditions which occur outside the application, such as crash of the system. Runtime exceptions are usually occur by fault in the application logic. You can't do anything in these situations. When runtime exception occur, you have to re-write your program code. So, these are not checked by compiler. These runtime exceptions will uncover in development, and testing period. Then we have to refactor our code to remove these errors.

当发生异常时,您必须捕获并处理该异常,或者通过声明您的方法抛出该异常来告诉编译器您无法处理它,然后使用您的方法的代码将不得不处理该异常(即使它如果它不能处理它,也可以选择声明它抛出异常)。

编译器将检查我们是否完成了两件事之一(catch 或declare)。所以这些被称为检查异常。但是编译器不会检查错误和运行时异常(即使您可以选择捕获或声明,这不是必需的)。所以,这两个被称为 Unchecked 异常。

错误用于表示在应用程序之外发生的那些情况,例如系统崩溃。运行时异常通常是由应用程序逻辑中的错误引起的。在这些情况下你什么也做不了。当运行时异常发生时,您必须重新编写程序代码。因此,编译器不会检查这些。这些运行时异常将在开发和测试期间发现。然后我们必须重构我们的代码以消除这些错误。

回答by Espo

The rule I use is: never use unchecked exceptions! (or when you don't see any way around it)

我使用的规则是:永远不要使用未经检查的异常!(或者当你看不到任何方法时)

From the point of view of the developer using your library or the end-user using your library/application it really sucks to be confronted with an application that crashes due to an uncought exception. And counting on a catch-all is no good either.

从使用您的库的开发人员或使用您的库/应用程序的最终用户的角度来看,遇到由于意外异常而崩溃的应用程序真的很糟糕。指望包罗万象也不好。

This way the end user can still be presented with an error message, instead of the application completely disappearing.

这样,最终用户仍然可以看到错误消息,而不是应用程序完全消失。

回答by David Crow

Checked exceptions are useful for recoverable cases where you want to provide information to the caller (i.e. insufficient permissions, file not found, etc).

检查异常对于您想向调用者提供信息(即权限不足、未找到文件等)的可恢复情况很有用。

Unchecked exceptions are used rarely, if at all, for informing the user or programmer of serious errors or unexpected conditions during run-time. Don't throw them if you're writing code or libraries that will be used by others, as they may not be expecting your software to throw unchecked exceptions since the compiler doesn't force them to be caught or declared.

未经检查的异常很少(如果有的话)用于在运行时通知用户或程序员严重错误或意外情况。如果您正在编写将被其他人使用的代码或库,请不要抛出它们,因为他们可能不希望您的软件抛出未经检查的异常,因为编译器不会强制捕获或声明它们。

回答by Konrad Rudolph

The rule I use is: never use unchecked exceptions! (or when you don't see any way around it)

我使用的规则是:永远不要使用未经检查的异常!(或者当你看不到任何方法时)

There's a very strong case for the opposite: Never use checked exceptions. I'm reluctant to take sides in the debate but there seems to be a broad consensus that introducing checked exceptions was a wrong decision in hindsight. Please don't shoot the messenger and refer to thosearguments.

有一个非常强烈的相反情况:永远不要使用检查异常。我不愿意在辩论中偏袒任何一方,但似乎有一个广泛的共识,即事后看来,引入受检异常是一个错误的决定。请不要射击信使并参考这些论点

回答by VonC

Here is my 'final rule of thumb'.
I use:

这是我的“最终经验法则”。
我用:

  • unchecked exceptionwithin the code of my method for a failure due to the caller(that involves an explicit and complete documentation)
  • checked exceptionfor a failure due to the calleethat I need to make explicit to anyone wanting to use my code
  • 由于调用者而导致失败的方法代码中的未经检查的异常(涉及明确且完整的文档
  • 检查异常故障,由于被叫,我需要作出明确任何人想使用我的代码

Compare to the previous answer, this is a clear rationale (upon which one can agree or disagree) for the use of one or the other (or both) kind of exceptions.

与之前的答案相比,这是使用一种或另一种(或两种)例外的明确理由(可以同意或不同意)。



For both of those exceptions, I will create my own unchecked and checked Exception for my application (a good practice, as mentionned here), except for very common unchecked exception (like NullPointerException)

对于这两个异常,我将为我的应用程序创建我自己的未检查和已检查异常(一个很好的做法,正如这里提到的),除了非常常见的未检查异常(如 NullPointerException)

So for instance, the goal of this particular function below is to make (or get if already exist) an object,
meaning:

因此,例如,下面这个特定函数的目标是创建(或获取,如果已经存在)一个对象,
这意味着:

  • the container of the object to make/get MUST exist (responsibility of the CALLER
    => unchecked exception, AND clear javadoc comment for this called function)
  • the other parameters can not be null
    (choice of the coder to put that on the CALLER: the coder will not check for null parameter but the coder DOES DOCUMENT IT)
  • the result CAN NOT BE NULL
    (responsibility and choice of the code of the callee, choice which will be of great interest for the caller
    => checked exception because every callers MUST take a decision if the object can not be created/found, and that decision must be enforced at the compilation time: they can not use this function without having to deal with this possibility, meaning with this checkedexception).
  • 要生成/获取的对象的容器必须存在(CALLER 的责任
    => 未检查异常,并清除此被调用函数的 javadoc 注释)
  • 其他参数不能为空
    (编码器选择将其放在 CALLER 上:编码器不会检查空参数,但编码器会记录它)
  • 结果不能为空
    (被调用者的代码的责任和选择,调用者非常感兴趣的选择
    => 检查异常,因为如果无法创建/找到对象,每个调用者都必须做出决定,并且必须在编译时强制执行决定:他们不能使用这个函数而不必处理这种可能性,这意味着有这个检查的异常)。

Example:

例子:



/**
 * Build a folder. <br />
 * Folder located under a Parent Folder (either RootFolder or an existing Folder)
 * @param aFolderName name of folder
 * @param aPVob project vob containing folder (MUST NOT BE NULL)
 * @param aParent parent folder containing folder 
 *        (MUST NOT BE NULL, MUST BE IN THE SAME PVOB than aPvob)
 * @param aComment comment for folder (MUST NOT BE NULL)
 * @return a new folder or an existing one
 * @throws CCException if any problems occurs during folder creation
 * @throws AssertionFailedException if aParent is not in the same PVob
 * @throws NullPointerException if aPVob or aParent or aComment is null
 */
static public Folder makeOrGetFolder(final String aFoldername, final Folder aParent,
    final IPVob aPVob, final Comment aComment) throws CCException {
    Folder aFolderRes = null;
    if (aPVob.equals(aParent.getPVob() == false) { 
       // UNCHECKED EXCEPTION because the caller failed to live up
       // to the documented entry criteria for this function
       Assert.isLegal(false, "parent Folder must be in the same PVob than " + aPVob); }

    final String ctcmd = "mkfolder " + aComment.getCommentOption() + 
        " -in " + getPNameFromRepoObject(aParent) + " " + aPVob.getFullName(aFolderName);

    final Status st = getCleartool().executeCmd(ctcmd);

    if (st.status || StringUtils.strictContains(st.message,"already exists.")) {
        aFolderRes = Folder.getFolder(aFolderName, aPVob);
    }
    else {
        // CHECKED EXCEPTION because the callee failed to respect his contract
        throw new CCException.Error("Unable to make/get folder '" + aFolderName + "'");
    }
    return aFolderRes;
}

回答by Stephane

On any large enough system, with many layers, checked exception are useless as, anyway, you need an architectural level strategy to handle how the exception will be handled (use a fault barrier)

在任何足够大的系统上,有很多层,检查异常是无用的,因为无论如何,您需要一个架构级别的策略来处理如何处理异常(使用故障屏障)

With checked exceptions your error handling stategy is micro-managed and its unbearable on any large system.

通过检查异常,您的错误处理策略是微管理的,在任何大型系统上都是无法忍受的。

Most of the time you don't know if an error is "recoverable" because you don't know in what layer the caller of your API is located.

大多数情况下,您不知道错误是否“可恢复”,因为您不知道 API 的调用方位于哪一层。

Let's say that I create a StringToInt API that converts the string representation of an integer to an Int. Must I throw a checked exception if the API is called with the "foo" string ? Is it recoverable ? I don't know because in his layer the caller of my StringToInt API may already have validated the input, and if this exception is thrown it's either a bug or a data corruption and it isn't recoverable for this layer.

假设我创建了一个 StringToInt API,它将整数的字符串表示形式转换为 Int。如果使用“foo”字符串调用 API,我是否必须抛出已检查的异常?可以恢复吗?我不知道,因为在他的层中,我的 StringToInt API 的调用者可能已经验证了输入,如果抛出此异常,则它要么是错误,要么是数据损坏,并且该层无法恢复。

In this case the caller of the API does not want to catch the exception. He only wants to let the exception "bubble up". If I chose a checked exception, this caller will have plenty of useless catch block only to artificially rethrow the exception.

在这种情况下,API 的调用者不想捕获异常。他只想让异常“冒泡”。如果我选择了检查异常,这个调用者将有大量无用的 catch 块,只是为了人为地重新抛出异常。

What is recoverable depends most of the time on the caller of the API, not on the writter of the API. An API should not use checked exceptions as only unchecked exceptions allows to choose to either catch or ignore an exception.

什么是可恢复的,大部分时间取决于 API 的调用者,而不是 API 的编写者。API 不应使用已检查异常,因为只有未检查异常才允许选择捕获或忽略异常。

回答by Yoni

It's not just a matter of the ability to recover from the exception. What matter most, in my opinion, is whether the caller is interested in catching the exception or not.

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

If you write a library to be used elsewhere, or a lower-level layer in your application, ask yourself if the caller is interested in catching (knowing about) your exception. If he is not, then use an unchecked exception, so you don't burden him unnecessarily.

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

This is the philosophy used by many frameworks. Spring and hibernate, in particularly, come to mind - they convert known checked exception to unchecked exception precisely because checked exceptions are overused in Java. One example that I can think of is the JSONException from json.org, which is a checked exception and is mostly annoying - it should be unchecked, but the developer simply haven't thought it through.

这是许多框架使用的哲学。特别是 Spring 和 hibernate - 它们将已知的已检查异常转换为未检查异常正是因为在 Java 中过度使用了已检查异常。我能想到的一个例子是来自 json.org 的 JSONException,它是一个受检查的异常,而且很烦人——它应该是未经检查的,但开发人员根本没有考虑清楚。

By the way, most of the time the caller's interest in the exception is directly correlated to the ability to recover from the exception, but that is not always the case.

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

回答by OscarRyz

You're correct.

你是对的。

Unchecked exceptionsare used to let the system fail fastwhich is a good thing. You should clearly state what is your method expecting in order to work properly. This way you can validate the input only once.

未经检查的异常用于让系统快速失败,这是一件好事。你应该清楚地说明你的方法期望什么才能正常工作。这样,您只能验证输入一次。

For instance:

例如:

/**
 * @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"
      .....  
 }

Just to put an example. The point is, if the system fails fast, then you'll know where and why it did fail. You'll get an stacktrace like:

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

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

And you'll know what happened. The OtherClass in the "delegateTheWork" method ( at line 4569 ) called your class with the "exit" value, even when it shouldn't etc.

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

Otherwise you would have to sprinkle validations all over your code and that's error prone. Plus, sometimes it is hard to track what went wrong and you may expect hours of frustrating debugging

否则,您将不得不在整个代码中进行验证,这很容易出错。另外,有时很难跟踪出了什么问题,而且您可能需要数小时的令人沮丧的调试

Same thing happens with NullPointerExceptions. If you have a 700 lines class with some 15 methods, that uses 30 attributes and none of them can be null, instead of validating in each of those methods for nullability you could make all those attributes read-only and validate them in the constructor or factory method.

NullPointerExceptions 也会发生同样的事情。如果您有一个包含 15 个方法的 700 行类,该类使用 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 
      ....
  }

Checked exceptionsAre useful when the programmer ( you or your co-workers ) did everything right, validated the input, ran tests, and all the code is perfect, but the code connects to a third party webservice that may be down ( or a file you were using was deleted by another external process etc ) . The webservice may even be validate before the connection is attempted, but during the data transfer something went wrong.

检查异常当程序员(您或您的同事)做对了一切,验证输入,运行测试,并且所有代码都完美无缺,但代码连接到可能已关闭的第三方 Web 服务(或文件您正在使用被另一个外部进程删除等)。甚至可能在尝试连接之前验证网络服务,但在数据传输期间出现问题。

In that scenario there is nothing that you or your co-workers can do to help it. But still you have to do something and not let the application just die and disappear in the eyes of the user. You use a checked exception for that and handle the exception, what can you do when that happens?, most of the time , just to attempt to log the error, probably save your work ( the app work ) and present a message to the user. ( The site blabla is down, please retry later etc. )

在这种情况下,您或您的同事无能为力。但是您仍然必须做一些事情,不要让应用程序在用户眼中消失。您为此使用已检查的异常并处理异常,发生这种情况时您能做什么?,大多数情况下,只是为了尝试记录错误,可能保存您的工作(应用程序工作)并向用户显示一条消息. (网站blabla已关闭,请稍后重试等)

If the checked exception are overused ( by adding the "throw Exception" in the all the methods signatures ) , then your code will become very fragile, because everyone will ignore that exception ( because is too general ) and the quality of code will be seriously compromised.

如果检查异常被过度使用(通过在所有方法签名中添加“throw Exception”),那么您的代码将变得非常脆弱,因为每个人都会忽略该异常(因为太笼统了)并且代码质量会受到严重影响妥协了。

If you overuse unchecked exception something similar will happen. The users of that code don't know if something may go wrong an a lot of try{...}catch( Throwable t ) will appear.

如果您过度使用未经检查的异常,则会发生类似的情况。该代码的用户不知道是否会出错,很多 try{...}catch( Throwable t ) 会出现。

回答by Colin Saxton

You can call it a checked or unchecked exception; however, bothtypes of exception can be caught by the programmer, so the best answer is: write allof your exceptions as uncheckedand document them. That way the developer who uses your API can choose whether he or she wants to catch that exception and do something. Checked exceptions are a complete waste of everyone's time and it makes your code a shocking nightmare to look at. Proper unit testing will then bring up any exceptions that you may have to catch and do something with.

您可以将其称为已检查或未检查的异常;然而,这两种类型的异常都可以被程序员捕获,所以最好的答案是:将所有异常写成未经检查并记录下来。这样,使用您的 API 的开发人员可以选择是否要捕获该异常并执行某些操作。检查异常完全是在浪费每个人的时间,它使您的代码看起来像是一场令人震惊的噩梦。然后,适当的单元测试将提出您可能必须捕获并处理的任何异常。

回答by Reachgoals

Checked Exception:If client can recover from an exception and would like to continue, use checked exception.

检查异常:如果客户端可以从异常中恢复并想继续,请使用检查异常。

Unchecked Exception:If a client can't do any thing after the exception, then raise unchecked exception.

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

Example: If you are expected to do arithmetic operation in a method A() and based on the output from A(), you have to another operation. If the output is null from method A() which you are not expecting during the run time, then you are expected to throw Null pointer Exception which is Run time exception.

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

Refer here

参考这里