约书亚·布洛赫在《有效的Java》中说过

为以下情况使用检查异常 可恢复条件和运行时 编程错误的例外 (第二版第58项)

看看我理解的对不对。

以下是我对受控异常的理解:

try{
    String userInput = //read in user input
    Long id = Long.parseLong(userInput);
}catch(NumberFormatException e){
    id = 0; //recover the situation by setting the id to 0
}

1. 上述异常是否被认为是受控异常?

2. RuntimeException是未检查的异常吗?

以下是我对未检查异常的理解:

try{
    File file = new File("my/file/path");
    FileInputStream fis = new FileInputStream(file);   
}catch(FileNotFoundException e){

//3. What should I do here?
    //Should I "throw new FileNotFoundException("File not found");"?
    //Should I log?
    //Or should I System.exit(0);?
}

4. 现在,上面的代码不能也是一个受控异常吗?我可以试着挽回这样的局面吗?我可以吗?(注:我的第三个问题在上面的陷阱里)

try{
    String filePath = //read in from user input file path
    File file = new File(filePath);
    FileInputStream fis = new FileInputStream(file);   
}catch(FileNotFoundException e){
    //Kindly prompt the user an error message
    //Somehow ask the user to re-enter the file path.
}

5. 人们为什么要这样做?

public void someMethod throws Exception{

}

为什么他们让异常冒出来?早点处理错误不是更好吗?为什么要冒出来?

6. 我是否应该冒泡出确切的异常或使用异常掩盖它?

以下是我的阅读资料

在Java中,什么时候应该创建检查异常,什么时候应该是运行时异常?

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


当前回答

我只是想添加一些根本不使用受控异常的理由。这不是一个完整的答案,但我觉得它确实回答了你的部分问题,并补充了许多其他的答案。

Whenever checked exceptions are involved, there's a throws CheckedException somewhere in a method signature (CheckedException could be any checked exception). A signature does NOT throw an Exception, throwing Exceptions is an aspect of implementation. Interfaces, method signatures, parent classes, all these things should NOT depend on their implementations. The usage of checked Exceptions here (actually the fact that you have to declare the throws in the method signature) is binding your higher-level interfaces with your implementations of these interfaces.

让我给你们看一个例子。

让我们有一个像这样漂亮干净的界面

public interface IFoo {
    public void foo();
}

现在我们可以编写方法foo()的许多实现,就像这样

public class Foo implements IFoo {
    @Override
    public void foo() {
        System.out.println("I don't throw and exception");
    }
}

类Foo完全没问题。现在让我们第一次尝试Bar类

public class Bar implements IFoo {
    @Override
    public void foo() {
        //I'm using InterruptedExcepton because you probably heard about it somewhere. It's a checked exception. Any checked exception will work the same.
        throw new InterruptedException();
    }
}

这个类Bar不能编译。由于InterruptedException是一个已检查异常,您必须捕获它(在方法foo()中使用try-catch)或声明您正在抛出它(在方法签名中添加抛出InterruptedException)。因为我不想在这里捕获这个异常(我希望它向上传播,这样我就可以在其他地方正确地处理它),让我们改变签名。

public class Bar implements IFoo {
    @Override
    public void foo() throws InterruptedException {
        throw new InterruptedException();
    }
}

这个类Bar也不能编译!Bar的方法foo()不会覆盖IFoo的方法foo(),因为它们的签名不同。我可以删除@Override注释,但我想编程接口IFoo像IFoo foo;然后再决定使用哪个实现,比如foo = new Bar();如果Bar的方法foo()没有覆盖IFoo的方法foo,当我执行foo.foo();它不会调用Bar的foo()实现。

To make Bar's public void foo() throws InterruptedException override IFoo's public void foo() I MUST add throws InterruptedException to IFoo's method signature. This, however, will cause problems with my Foo class, since it's foo() method's signature differs from IFoo's method signature. Furthermore, if I added throws InterruptedException to Foo's method foo() I would get another error stating that Foo's method foo() declares that it throws an InterruptedException yet it never throws an InterruptedException.

正如您所看到的(如果我在解释这些东西方面做得不错的话),抛出InterruptedException这样的检查异常的事实迫使我将我的接口IFoo绑定到它的一个实现上,这反过来又会对IFoo的其他实现造成严重破坏!

这就是受控异常很糟糕的一个重要原因。在帽。

一种解决方案是捕获已检查异常,将其包装在未检查的异常中,然后抛出未检查的异常。

其他回答

关于未检查异常和已检查异常之间的区别,我最喜欢的描述来自Java教程的试用文章“未检查异常-争议”(很抱歉在这篇文章中介绍了所有基本的内容-但是,嘿,基本的有时是最好的):

这是底线原则:如果客户可以合理地 希望从异常中恢复,使其成为受控异常。如果 客户端不能做任何事情来从异常中恢复 未经检查的异常

“抛出哪种类型的异常”的核心是语义的(在某种程度上),上面的引用提供了一个很好的指导方针(因此,我仍然被c#摆脱受控异常的概念所震撼——特别是Liskov认为它们有用)。

接下来的问题就变得合乎逻辑了:编译器希望我显式地响应哪些异常?你希望客户能从中恢复过来。

1 . 如果你不确定一个异常,检查API:

java . lang . object 由java.lang.Throwable扩展 由java.lang.Exception扩展 //<-NumberFormatException是一个RuntimeException 由java.lang.IllegalArgumentException扩展 由java.lang.NumberFormatException扩展

2。是的,以及所有扩展它的异常。

3.不需要捕获和抛出相同的异常。在这种情况下,您可以显示一个新的文件对话框。

4所示。FileNotFoundException已经是一个检查异常。

5。如果期望调用someMethod的方法捕获异常,则可以抛出后者。它只是“传球”。使用它的一个例子是,如果你想在你自己的私有方法中抛出它,而在你的公共方法中处理异常。

一个很好的阅读是Oracle文档本身:http://download.oracle.com/javase/tutorial/essential/exceptions/runtime.html

Why did the designers decide to force a method to specify all uncaught checked exceptions that can be thrown within its scope? Any Exception that can be thrown by a method is part of the method's public programming interface. Those who call a method must know about the exceptions that a method can throw so that they can decide what to do about them. These exceptions are as much a part of that method's programming interface as its parameters and return value. The next question might be: "If it's so good to document a method's API, including the exceptions it can throw, why not specify runtime exceptions too?" Runtime exceptions represent problems that are the result of a programming problem, and as such, the API client code cannot reasonably be expected to recover from them or to handle them in any way. Such problems include arithmetic exceptions, such as dividing by zero; pointer exceptions, such as trying to access an object through a null reference; and indexing exceptions, such as attempting to access an array element through an index that is too large or too small.

在Java语言规范中还有一些重要的信息:

在throws子句中命名的受控异常类是方法或构造函数的实现者和用户之间契约的一部分。

IMHO的底线是,您可以捕获任何RuntimeException,但不需要这样做,事实上,实现不需要维护抛出的相同的未检查异常,因为这些异常不是契约的一部分。

许多人说检查异常(即你应该显式地捕获或重新抛出的异常)根本不应该使用。例如,它们在c#中被淘汰了,大多数语言都没有它们。因此,您总是可以抛出RuntimeException的子类(未检查的异常)

然而,我认为受控异常是有用的——当你想强迫API的用户思考如何处理异常情况(如果它是可恢复的)时,就使用它们。只是受控异常在Java平台中被过度使用了,这让人们讨厌它们。

以下是我对这个话题的扩展观点。

关于具体问题:

Is the NumberFormatException consider a checked exception? No. NumberFormatException is unchecked (= is subclass of RuntimeException). Why? I don't know. (but there should have been a method isValidInteger(..)) Is RuntimeException an unchecked exception? Yes, exactly. What should I do here? It depends on where this code is and what you want to happen. If it is in the UI layer - catch it and show a warning; if it's in the service layer - don't catch it at all - let it bubble. Just don't swallow the exception. If an exception occurs in most of the cases you should choose one of these: log it and return rethrow it (declare it to be thrown by the method) construct a new exception by passing the current one in constructor Now, couldn't the above code also be a checked exception? I can try to recover the situation like this? Can I? It could've been. But nothing stops you from catching the unchecked exception as well Why do people add class Exception in the throws clause? Most often because people are lazy to consider what to catch and what to rethrow. Throwing Exception is a bad practice and should be avoided.

遗憾的是,没有单一的规则可以让您决定何时捕获、何时重新抛出、何时使用已检查异常和何时使用未检查异常。我同意这会导致很多混乱和很多糟糕的代码。布洛赫阐述了总体原则(你引用了其中的一部分)。一般的原则是将异常重新抛出到可以处理它的层。

简而言之,你的模块或上面的模块在运行时应该处理的异常被称为受控异常;其他是未检查的异常,它们是RuntimeException或Error。

在本视频中,它解释了Java中的受控异常和未受控异常: https://www.youtube.com/watch?v=ue2pOqLaArw

Java distinguishes between two categories of exceptions (checked & unchecked). Java enforces a catch or declared requirement for checked exceptions. An exception's type determines whether an exception is checked or unchecked. All exception types that are direct or indirect subclasses of class RuntimeException are unchecked exception. All classes that inherit from class Exception but not RuntimeException are considered to be checked exceptions. Classes that inherit from class Error are considered to be unchecked. Compiler checks each method call and deceleration to determine whether the method throws checked exception. If so the compiler ensures the exception is caught or is declared in a throws clause. To satisfy the declare part of the catch-or-declare requirement, the method that generates the exception must provide a throws clause containing the checked-exception. Exception classes are defined to be checked when they are considered important enough to catch or declare.