约书亚·布洛赫在《有效的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 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.

检查异常:

The exceptions which are checked by the compiler for smooth execution of the program at runtime are called Checked Exception. These occur at compile time. If these are not handled properly, they will give compile time error (Not Exception). All subclasses of Exception class except RuntimeException are Checked Exception. Hypothetical Example - Suppose you are leaving your house for the exam, but if you check whether you took your Hall Ticket at home(compile time) then there won't be any problem at Exam Hall(runtime).

未检查异常:

The exceptions which are not checked by the compiler are called Unchecked Exceptions. These occur at runtime. If these exceptions are not handled properly, they don’t give compile time error. But the program will be terminated prematurely at runtime. All subclasses of RunTimeException and Error are unchecked exceptions. Hypothetical Example - Suppose you are in your exam hall but somehow your school had a fire accident (means at runtime) where you can't do anything at that time but precautions can be made before (compile time).

所有这些都是受控异常。未检查的异常是RuntimeException的子类。问题不在于如何处理它们,而在于你的代码是否应该抛出它们。如果你不想让编译器告诉你你还没有处理一个异常,那么你可以使用一个未检查的(RuntimeException的子类)异常。这些应该保存在你无法恢复的情况下,比如内存不足等。

已检查-容易发生。在编译时选中。

小薇。FileOperations

未检查-由于不良数据。在运行时签入。

如. .

String s = "abc";
Object o = s;
Integer i = (Integer) o;

Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
    at Sample.main(Sample.java:9)

这里的异常是由于错误的数据,在编译时无法确定。

上述异常是否被认为是受控异常? 没有 如果异常是RuntimeException,那么您正在处理的异常并不会使其成为Checked exception。 RuntimeException是未检查的异常吗? 是的

受控异常是java.lang.Exception的子类 未检查异常是java.lang.RuntimeException的子类

抛出已检查异常的调用需要包含在try{}块中,或者在方法调用方的更高级别中处理。在这种情况下,当前方法必须声明它抛出上述异常,以便调用者可以做出适当的安排来处理异常。

希望这能有所帮助。

问:我应该把确切的泡沫 异常或屏蔽它使用异常?

A:是的,这是一个非常好的问题,也是重要的设计考虑因素。Exception类是一个非常通用的异常类,可用于包装内部低级异常。您最好创建一个自定义异常,并将其封装在其中。但是,还有一个很大的问题——永远不要模糊潜在的根本原因。对于前任,不要做下面的事情

try {
     attemptLogin(userCredentials);
} catch (SQLException sqle) {
     throw new LoginFailureException("Cannot login!!"); //<-- Eat away original root cause, thus obscuring underlying problem.
}

你可以这样做:

try {
     attemptLogin(userCredentials);
} catch (SQLException sqle) {
     throw new LoginFailureException(sqle); //<-- Wrap original exception to pass on root cause upstairs!.
}

对生产支持团队来说,消除原始的根本原因,掩盖无法恢复的实际原因是一场噩梦,因为他们只能访问应用程序日志和错误消息。 虽然后者是一种更好的设计,但许多人不经常使用它,因为开发人员无法将底层消息传递给调用者。因此,请明确指出:无论是否封装在任何特定于应用程序的异常中,始终将实际异常传递回去。

在尝试捕获runtimeexception时

runtimeexception作为一般规则不应该被尝试捕获。它们通常是一个编程错误的信号,应该被置之不理。相反,程序员应该在调用一些可能导致RuntimeException的代码之前检查错误条件。为例:

try {
    setStatusMessage("Hello Mr. " + userObject.getName() + ", Welcome to my site!);
} catch (NullPointerException npe) {
   sendError("Sorry, your userObject was null. Please contact customer care.");
}

这是一种糟糕的编程实践。相反,null检查应该像-那样执行

if (userObject != null) {
    setStatusMessage("Hello Mr. " + userObject.getName() + ", Welome to my site!);
} else {
   sendError("Sorry, your userObject was null. Please contact customer care.");
}

但有时这种错误检查是昂贵的,例如数字格式,考虑这个-

try {
    String userAge = (String)request.getParameter("age");
    userObject.setAge(Integer.parseInt(strUserAge));
} catch (NumberFormatException npe) {
   sendError("Sorry, Age is supposed to be an Integer. Please try again.");
}

在这里,预调用错误检查不值得花费精力,因为它本质上意味着复制parseInt()方法中的所有字符串到整数转换代码——如果由开发人员实现,则很容易出错。因此,最好是取消try-catch。

因此NullPointerException和NumberFormatException都是runtimeexception,捕获一个NullPointerException应该替换为一个优雅的空检查,而我建议显式捕获NumberFormatException以避免可能引入容易出错的代码。