何时选择受控异常和非受控异常

发布于 2024-07-04 13:21:53 字数 132 浏览 12 评论 0原文

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

我的直觉是,如果调用者能够以某种有效的方式恢复,则需要检查异常,而对于不可恢复的情况,则需要检查异常,但我对其他人的想法感兴趣。

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?

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(18

我的影子我的梦 2024-07-11 13:21:53

我认为我们可以从几个问题来思考例外:

为什么会发生例外? 当发生这种情况时我们该怎么办

错误,一个bug。比如调用了null对象的方法。

String name = null;
... // some logics
System.out.print(name.length()); // name is still null here

这种异常应该在测试时修复。 否则,它会破坏生产,并且会出现非常高的错误,需要立即修复。 这种异常不需要检查。

通过外部输入,您无法控制或信任外部服务的输出。

String name = ExternalService.getName(); // return null
System.out.print(name.length());    // name is null here

这里,如果 name 为 null 时你想继续,你可能需要检查 name 是否为 null,否则,你可以不管它,它会停在这里并给调用者提供运行时异常。
这种异常不需要检查。

由于来自外部的运行时异常,您无法控制或信任外部服务。

在这里,如果你想在发生时继续,你可能需要捕获来自ExternalService的所有异常,否则,你可以不管它,它会停在这里并向调用者提供运行时异常。

通过检查来自外部的异常,您无法控制或信任外部服务。

在这里,如果你想在发生时继续,你可能需要捕获来自ExternalService的所有异常,否则,你可以不管它,它会停在这里并向调用者提供运行时异常。

这种情况下,我们是否需要知道ExternalService中发生了什么样的异常?这取决于:

  1. 如果你能处理某种异常,你就需要捕获它们并进行处理。 对于其他情况,将它们冒泡。

  2. 如果您需要记录或响应用户的特定执行,您可以捕获它们。 对于其他人,将它们冒泡。

I think we can think about exeptions from several questions:

why does exeption happen? What can we do when it happens

by mistake, a bug. such as a method of null object is called.

String name = null;
... // some logics
System.out.print(name.length()); // name is still null here

This kind of exception should be fixed during test. Otherwise, it breaks the production, and you got a very high bug which needs to be fixed immediately. This kind of exceptions do not need be checked.

by input from external, you cannot control or trust the output of external service.

String name = ExternalService.getName(); // return null
System.out.print(name.length());    // name is null here

Here, you may need to check whether the name is null if you want to continue when it is null, otherwise, you can let it alone and it will stop here and give the caller the runtime exception.
This kind of exceptions do not need be checked.

by runtime exception from external, you cannot control or trust the external service.

Here, you may need to catch all exceptions from ExternalService if you want to continue when it happens, otherwise, you can let it alone and it will stop here and give the caller the runtime exception.

by checked exception from external, you cannot control or trust the external service.

Here, you may need to catch all exceptions from ExternalService if you want to continue when it happens, otherwise, you can let it alone and it will stop here and give the caller the runtime exception.

In this case, do we need to know what kind of exception happened in ExternalService? It depends:

  1. if you can handle some kinds of exceptions, you need to catch them and process. For others, bubble them.

  2. if you need log or response to user the specific execption, you can catch them. For others, bubble them.

倒带 2024-07-11 13:21:53

当异常不太可能出现时,即使在捕获异常之后我们也可以继续,并且我们无法采取任何措施来避免该异常,那么我们可以使用检查异常。

每当我们想要在特定异常发生时做一些有意义的事情,并且当该异常是预期的但不确定时,那么我们可以使用检查异常。

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

运行时异常用于最有可能发生异常、无法进一步处理且无法恢复的情况。 因此,在这种情况下,我们可以针对该异常采取预防措施。 例如:NUllPointerException、ArrayOutofBoundsException。 这些更有可能发生。 在这种情况下,我们可以在编码时采取预防措施来避免此类异常。 否则我们将不得不在每个地方编写 try catch 块。

可以不检查更一般的例外情况,检查不太一般的例外情况。

Whnever an exception is less likely expected, and we can proceed even after catching that, and we can not do anything to avoid that exception then we can use checked exception.

Whenever we want to do something meaningful when a particular exceptions happens and when that exception is expected but not certain, then we can use checked exception.

Whenever exception navigating in different layers, we don't need to catch it in every layer, in that case, we can use runtime exception or wrap exception as unchecked exception.

Runtime exception is used when exception most likely to be happened, there is no way of going further and nothing can be recoverable. So in this case we can take precautions with respect to that exception. EX: NUllPointerException, ArrayOutofBoundsException. These are more likely to happen. In this scenario, we can take precautions while coding to avoid such exception. Otherwise we will have to write try catch blocks every where.

More general exceptions can be made Unchecked, less general are checked.

花期渐远 2024-07-11 13:21:53

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

从使用您的库的开发人员或使用您的库/应用程序的最终用户的角度来看,面对因不可预见的异常而崩溃的应用程序确实很糟糕。 指望包罗万象也不好。

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

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.

今天小雨转甜 2024-07-11 13:21:53

我认为在声明应用程序异常时,它应该是未经检查的异常,即运行时异常的子类。
原因是它不会因方法上的 try-catch 和 throws 声明而使应用程序代码变得混乱。 如果您的应用程序使用 Java Api,它会抛出无论如何都需要处理的已检查异常。 对于其他情况,应用程序可以抛出未经检查的异常。 如果应用程序调用者仍然需要处理未检查的异常,那么可以这样做。

I think when declaring Application Exception it should be Unchecked Exception i.e., subclass of RuntimeException.
The reason is it will not clutter application code with try-catch and throws declaration on method. If your application is using Java Api which throws checked exceptions that anyways need to be handle. For other cases, the application can throw unchecked exception. If the application caller still needs to handle unchecked exception, it can be done.

长发绾君心 2024-07-11 13:21:53

作为一项规则,我同意对未检查异常的偏好,尤其是在设计 API 时。 调用者始终可以选择捕获记录的、未经检查的异常。 您只是没有不必要地强迫呼叫者这样做。

我发现检查异常在较低级别很有用,作为实现细节。 与必须管理指定的错误“返回代码”相比,它通常看起来是更好的控制流机制。 有时它也可以帮助了解低级代码更改的想法的影响...在下游声明一个已检查的异常并查看谁需要调整。 如果有很多通用的:catch(Exception e)抛出异常,那么最后一点就不适用,无论如何,这些通常都不是经过深思熟虑的。

I agree with the preference for unchecked exceptions as a rule, especially when designing an API. The caller can always choose to catch a documented, unchecked exception. You're just not needlessly forcing the caller to.

I find checked exceptions useful at the lower-level, as implementation detail. It often seems like a better flow of control mechanism than having to manage a specified error "return code". It can sometimes help see the impact of an idea for a low level code change too... declare a checked exception downstream and see who would need to adjust. This last point doesn't apply if there are a lot of generic: catch(Exception e) or throws Exception which is usually not too well-thought out anyway.

迟到的我 2024-07-11 13:21:53

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

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

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.

甜扑 2024-07-11 13:21:53

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

未经检查的异常:
如果客户端在异常后无法执行任何操作,则引发未经检查的异常。

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

请参阅此处

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.

Refer here

梦魇绽荼蘼 2024-07-11 13:21:53

这里我想分享一下我多年开发经验的看法:

  1. 检查异常。 这是业务用例或调用流程的一部分,这是我们期望或不期望的应用程序逻辑的一部分。 例如连接被拒绝、条件不满足等。我们需要处理它并向用户显示相应的消息,说明发生了什么以及下一步要做什么(稍后重试等)。
    我通常将其称为后处理异常或“用户”异常。

  2. 未经检查的异常。 这是编程异常的一部分,是软件代码编程中的一些错误(错误、缺陷),反映了程序员必须按照文档使用 API 的方式。 如果外部库/框架文档说它期望获取某个范围内的数据并且非空,因为将抛出 NPE 或 IllegalArgumentException,程序员应该期望它并按照文档正确使用 API。 否则会抛出异常。
    我通常将其称为预处理异常或“验证”异常。

按目标受众。 现在让我们谈谈已设计的异常的目标受众或人群(根据我的意见):

  1. 已检查异常。 目标受众是用户/客户。
  2. 未经检查的异常。 目标受众是开发人员。 换句话说,未经检查的异常是专为开发人员设计的。

按应用程序开发生命周期阶段。

  1. 检查异常被设计为在整个生产生命周期中存在,作为应用程序处理异常情况的正常和预期机制。
  2. 未经检查的异常被设计为仅在应用程序开发/测试生命周期期间存在,所有这些异常都应该在此期间修复,并且当应用程序已经在生产中运行时不应抛出。

框架通常使用未经检查的异常(例如Spring)的原因是框架无法确定应用程序的业务逻辑,这取决于开发人员捕获然后设计自己的逻辑。

Here is I want to share my opinion I have after many years of development experience:

  1. Checked exception. This is a part of business use case or call flow, this is a part of application logic we expect or not expect. For example connection rejected, condition is not satisfied etc. We need to handle it and show corresponding message to user with instructions what happened and what to do next (try again later etc).
    I usually call it post-processing exception or "user" exception.

  2. Unchecked exception. This is a part of programming exception, some mistake in software code programming (bug, defect) and reflects a way how programmers must use API as per documentation. If an external lib/framework doc says it expects to get data in some range and non null, because NPE or IllegalArgumentException will be thrown, programmer should expect it and use API correctly as per documentation. Otherwise the exception will be thrown.
    I usually call it pre-processing exception or "validation" exception.

By target audience. Now let's talk about target audience or group of people the exceptions have been designed (as per my opinion):

  1. Checked exception. Target audience is users/clients.
  2. Unchecked exception. Target audience is developers. By other words unchecked exception are designed for developers only.

By application development lifecycle phase.

  1. Checked exception is designed to exist during whole production lifecycle as normal and expected mechanism an application handles exceptional cases.
  2. Unchecked exception is designed to exist only during application development/testing lifecycle, all of them should be fixed during that time and should not be thrown when an application is running on production already.

The reason why frameworks usually use unchecked exceptions (Spring for example) is that framework cannot determine the business logic of your application, this is up to developers to catch then and design own logic.

陌伤浅笑 2024-07-11 13:21:53

这是解决“已检查/未检查”困境的非常简单的解决方案。

规则 1:将未检查异常视为代码执行前的可测试条件。
例如...

x.doSomething(); // the code throws a NullPointerException

其中 x 为空...
...代码可能应该具有以下内容...

if (x==null)
{
    //do something below to make sure when x.doSomething() is executed, it won’t throw a NullPointerException.
    x = new X();
}
x.doSomething();

规则 2:将受检异常视为代码执行时可能发生的不可测试的条件。

Socket s = new Socket(“google.com”, 80);
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();

…在上面的示例中,由于 DNS 服务器关闭,URL (google.com) 可能不可用。 即使在 DNS 服务器正在工作并将“google.com”名称解析为 IP 地址的那一刻,如果连接到 google.com,之后的任何时间,网络都可能出现故障。 在读取和写入流之前,您根本无法始终测试网络。

有时,我们必须执行代码才能知道是否存在问题。 通过强迫开发人员以这种方式编写代码,迫使他们通过检查异常来处理这些情况,我必须向发明这个概念的 Java 创建者致敬。

一般来说,Java中几乎所有的API都遵循上述2条规则。 如果您尝试写入文件,磁盘可能会在写入完成之前就已满。 其他进程可能导致磁盘已满。 根本没有办法测试这种情况。 对于那些与硬件交互的人来说,使用硬件随时可能会失败,检查异常似乎是解决这个问题的一个优雅的解决方案。

这存在一个灰色地带。 如果需要进行许多测试(令人兴奋的 if 语句包含大量 && 和 ||),抛出的异常将是 CheckedException ,因为它太痛苦了 - 你根本无法做到这一点说这个问题是一个编程错误。 如果测试次数远少于 10 个(例如“if (x == null)”),那么程序员错误应该是 UncheckedException。

与语言口译员打交道时,事情会变得有趣。 根据上述规则,语法错误应该被视为受检异常还是未经受检异常? 我认为,如果该语言的语法可以在执行之前进行测试,那么它应该是一个 UncheckedException。 如果无法测试该语言(类似于汇编代码在个人计算机上的运行方式),则语法错误应该是受检查的异常。

以上两条规则可能会消除您 90% 的选择问题。 要总结规则,请遵循以下模式......
1) 如果要执行的代码可以在执行之前进行测试以使其正确运行,并且如果发生异常(也称为程序员错误),则该异常应该是 UncheckedException(RuntimeException 的子类)。
2)如果要执行的代码在执行之前无法进行测试使其正确运行,则该异常应该是受检查的异常(异常的子类)。

Here is a very simple solution to your Checked/Unchecked dilemma.

Rule 1: Think of a Unchecked Exception as a testable condition before code executes.
for example…

x.doSomething(); // the code throws a NullPointerException

where x is null...
…the code should possibly have the following…

if (x==null)
{
    //do something below to make sure when x.doSomething() is executed, it won’t throw a NullPointerException.
    x = new X();
}
x.doSomething();

Rule 2: Think of a Checked Exception as an un-testable condition that may occur while the code executes.

Socket s = new Socket(“google.com”, 80);
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();

…in the example above, the URL (google.com) may be unavailable to due the DNS server being down. Even at the instant the DNS server was working and resolved the ‘google.com’ name to an IP address, if the connection is made to google.com, at anytime afterword, the network could go down. You simply can not test the network all the time before reading and writing to streams.

There are times where the code simply must execute before we can know if there is a problem. By forcing developers to write their code in such a way to force them to handle these situations via Checked Exception, I have to tip my hat to the creator of Java that invented this concept.

In general, almost all the APIs in Java follow the 2 rules above. If you try to write to a file, the disk could fill up before completing the write. It is possible that other processes had caused the disk to become full. There is simply no way to test for this situation. For those who interact with hardware where at any time, using the hardware can fail, Checked Exceptions seem to be an elegant solution to this problem.

There is a gray area to this. In the event that many tests are needed (a mind blowing if statement with lots of && and ||), the exception thrown will be a CheckedException simply because it’s too much of a pain to get right — you simply can’t say this problem is a programming error. If there are much less than 10 tests (e.g. ‘if (x == null)’), then the programmer error should be a UncheckedException.

Things get interesting when dealing with language interpreters. According to the rules above, should a Syntax Error be considered a Checked or Unchecked Exception? I would argue that if the syntax of the language can be tested before it gets executed, it should be an UncheckedException. If the language can not be tested — similar to how assembly code runs on a personal computer, then the Syntax Error should be a Checked Exception.

The 2 rules above will probably remove 90% of your concern over which to choose from. To summarize the rules, follow this pattern…
1) if the code to be execute can be tested before it’s executed for it to run correctly and if an Exception occurs — a.k.a. a programmer error, the Exception should be an UncheckedException (a subclass of RuntimeException).
2) if the code to be executed can not be tested before it’s executed for it to run correctly, the Exception should be a Checked Exception (a subclass of Exception).

一口甜 2024-07-11 13:21:53

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

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

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

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

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.

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.

爱格式化 2024-07-11 13:21:53

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

  • 如果错误是程序员错误,那么它一定是未经检查的异常例如:
    SQLException/IOException/NullPointerException。 这些例外是
    编程错误。 它们应该由程序员处理。 而在
    JDBC API,SQLException是检查异常,在Spring JDBCTemplate中
    这是一个未经检查的异常。程序员不用担心
    使用 Spring 时出现 SqlException。
  • 如果错误不是程序员错误且原因来自外部,则它一定是受检查的异常。例如:如果
    文件被删除或文件权限被其他人更改,它
    应该恢复。

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

检查异常可以通过两种方式处理。 这些使用 try-catch 或传播异常。 在传播异常的情况下,由于异常处理,调用堆栈中的所有方法将紧密耦合。 这就是为什么我们必须谨慎使用 Checked Exception。

如果您开发分层企业系统,则必须选择大多数未检查的异常来抛出,但不要忘记在您无法执行任何操作的情况下使用检查的异常。

We have to distinguish these two types of exception based on whether it is programmer error or not.

  • 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 is good example to understand subtle differences. FileNotFoundException is thrown in case file is not found. There are two reason for this exception. If the file path is defined by developer or taking from end user via GUI it should be an Unchecked Exception. If the file is deleted by someone else, it should be a Checked Exception.

Checked Exception can be handled in two ways. These are using try-catch or propagate the exception. In case of propagation of exception, all methods in call stack will be tightly coupled because of exception handling. That's why, we have to use Checked Exception carefully.

In case you develop an layered enterprise system, you have to choose mostly unchecked exception to throw, but don't forget to use checked exception for the case you cannot do anything.

爱的那么颓废 2024-07-11 13:21:53

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

You can call it a checked or unchecked exception; however, both types of exception can be caught by the programmer, so the best answer is: write all of your exceptions as unchecked and 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.

沉默的熊 2024-07-11 13:21:53

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

  • 我在方法代码中 未经检查的异常来处理由于调用者而导致的失败(涉及明确且完整的文档
  • 检查异常由于 被调用者 导致失败,我需要向任何想要使用我的 与之前的答案相比

,这是使用一种或另一种(或两种)例外的明确理由(人们可以同意或不同意)。


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

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

  • 要生成/获取的对象的容器必须存在(调用者的责任
    => 未经检查的异常,并清除此调用函数的 javadoc 注释)
  • 其他参数不能为 null
    (选择将其放在调用者上的编码器:编码器不会检查空参数,但编码器会记录它)
  • 结果不能为空
    (被调用者代码的责任和选择,调用者会非常感兴趣的选择
    => 检查异常,因为如果无法创建/找到对象,每个调用者都必须做出决定,并且必须在编译时强制执行该决定:他们不能在不处理这种可能性的情况下使用此函数,这意味着 检查异常)。

例子:


/**
 * 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;
}

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

  • unchecked exception within the code of my method for a failure due to the caller (that involves an explicit and complete documentation)
  • checked exception for a failure due to the callee that 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)

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 checked exception).

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;
}
原谅我要高飞 2024-07-11 13:21:53

你是对的。

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

例如:

/**
 * @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”值调用您的类,即使它不应该等等。

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

也会发生同样的情况。 如果您有一个 700 行的类,其中包含大约 15 个方法,使用 30 个属性,并且它们都不能为 null,那么您可以将所有这些属性设置为只读,然后在构造函数中验证它们,而不是在每个方法中验证可为空性,或者工厂方法。

 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已关闭,请稍后重试等。)

如果检查异常被过度使用(通过在所有方法签名中添加“抛出异常”),那么你的代码将变得非常脆弱,因为每个人都会忽略该异常(因为太笼统了)而代码的质量会受到严重损害。

如果过度使用未经检查的异常,就会发生类似的情况。 该代码的用户不知道是否会出现问题,因此会出现大量 try{...}catch( Throwable t ) 。

You're correct.

Unchecked exceptions are used to let the system fail fast which 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.

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.

 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 exceptions Are 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.

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. )

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.

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.

笑忘罢 2024-07-11 13:21:53

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

检查异常应用于可预测,但无法预防可以合理恢复的错误。

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

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

  1. 可预测但不可预防:调用者尽其所能来验证输入参数,但某些超出其控制范围的情况导致操作失败。 例如,您尝试读取一个文件,但在您检查文件是否存在和读取操作开始之间,有人将其删除。 通过声明受检查的异常,您可以告诉调用者预见到这种失败。
  2. 合理恢复:告诉调用者预测他们无法恢复的异常是没有意义的。 如果用户尝试读取不存在的文件,调用者可以提示他们输入新文件名。 另一方面,如果该方法由于编程错误(无效的方法参数或有错误的方法实现)而失败,则应用程序无法修复执行中的问题。 它能做的最好的事情就是记录问题并等待开发人员稍后修复它。

除非您抛出的异常满足上述所有条件,否则应使用未经检查的异常。

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

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

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 should be used for predictable, but unpreventable errors that are reasonable to recover from.

Unchecked Exceptions should 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.

Unless the exception you are throwing meets all of the above conditions it should use an 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 SQLException or IOException. Instead, it should wrap the exception in an abstraction that spans all implementations (e.g. RepositoryException).

云淡月浅 2024-07-11 13:21:53

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

有一种相反的情况:从不使用受检查的异常。 我不愿意在辩论中选边站(双方肯定都有很好的论据!),但事后看来,相当多的专家认为检查异常是一个错误的决定。

有关一些讨论,请查看 WikiWikiWeb 的“检查的异常具有可疑值”。 另一个早期广泛争论的例子是 Rod Waldhoff 的博客文章< /a>.

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

There’s a case for the opposite: never use checked exceptions. I’m reluctant to take sides in the debate (there’s definitely good arguments on both sides!) but a fair number of experts feel that checked exceptions were a wrong decision in hindsight.

For some discussion, check the WikiWikiWeb’s “Checked exceptions are of dubious value”. Another example of an early, extensive argument is Rod Waldhoff’s blog post.

眼中杀气 2024-07-11 13:21:53

来自Java 学习者

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

编译器将检查我们是否已经完成
两件事之一(抓住,或
宣布)。 所以这些被称为 Checked
例外情况。 但是错误和运行时
不检查异常
编译器(即使你可以选择
捕获或声明,它不是
必需的)。 所以,这两个被称为
未经检查的异常。

错误用于表示那些
发生在外部的条件
应用程序,例如崩溃
系统。 运行时异常是
通常是由于故障而发生的
应用逻辑。 你不能这样做
在这些情况下的任何事情。 什么时候
发生运行时异常,你必须
重新编写你的程序代码。 那么,这些
不被编译器检查。 这些
运行时异常将在
开发、测试期。 然后
我们必须重构我们的代码以删除
这些错误。

From A Java Learner:

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.

紫罗兰の梦幻 2024-07-11 13:21:53

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

使用检查的异常,您的错误处理策略是微管理的,并且这在任何大型系统上都是难以忍受的。

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

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

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

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

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.

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.

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.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文