约书亚·布洛赫在《有效的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中,什么时候应该创建检查异常,什么时候应该是运行时异常?

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


许多人说检查异常(即你应该显式地捕获或重新抛出的异常)根本不应该使用。例如,它们在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,那么您正在处理的异常并不会使其成为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以避免可能引入容易出错的代码。


某个异常是否为“受控异常”与是否捕获它或在捕获块中做了什么无关。它是异常类的属性。Exception的子类,除了RuntimeException及其子类,都是受控异常。

Java编译器迫使您要么捕获已检查的异常,要么在方法签名中声明它们。它本应提高程序的安全性,但大多数人的意见似乎是,它不值得它带来的设计问题。

为什么他们让异常冒泡 起来吗?不是处理错误越快 更好吗?为什么要冒出来?

因为这就是例外的意义所在。如果没有这种可能性,就不需要异常。它们使您能够在您选择的级别上处理错误,而不是强迫您在错误最初发生的低级方法中处理它们。


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


1)不,NumberFormatException是一个未检查的异常。即使你发现了它(你不需要),因为它是未检查的。这是因为它是IllegalArgumentException的子类,而IllegalArgumentException是RuntimeException的子类。

2) RuntimeException是所有未检查异常的根。RuntimeException的每个子类都未选中。所有其他异常和Throwable都被检查,除了错误(属于Throwable)。

3/4)你可以提醒用户他们选择了一个不存在的文件,并要求一个新的文件。或者停止通知用户他们输入了无效的内容。

5)抛出并捕获“异常”是不好的做法。但更一般地,您可能会抛出其他异常,以便调用者可以决定如何处理它。例如,如果您编写了一个库来处理读取一些文件输入,而您的方法被传递给一个不存在的文件,您不知道如何处理它。打电话的人是想再问一次还是想退出?因此,您将异常沿链向上抛出给调用者。

在许多情况下,出现未检查的异常是因为程序员没有验证输入(例如您的第一个问题中的NumberFormatException)。这就是为什么捕获它们是可选的,因为有更优雅的方法来避免生成这些异常。


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,但不需要这样做,事实上,实现不需要维护抛出的相同的未检查异常,因为这些异常不是契约的一部分。


为了回答最后一个问题(上面的其他问题似乎都得到了彻底的回答),“我应该冒泡出确切的异常还是使用exception掩盖它?”

我猜你的意思是这样的:

public void myMethod() throws Exception {
    // ... something that throws FileNotFoundException ...
}

不,总是声明尽可能精确的异常,或者一个这样的列表。你声明你的方法能够抛出的异常是你的方法和调用者之间契约的一部分。抛出"FileNotFoundException"意味着可能文件名无效,无法找到该文件;调用者需要聪明地处理这个问题。抛出异常意味着“嘿,该死的事情发生了。交易。”这是一个非常糟糕的API。

在第一篇文章的注释中有一些例子,其中“throws Exception”是一个有效且合理的声明,但对于您所编写的大多数“正常”代码来说,情况并非如此。


如果有人想要另一个不喜欢受控异常的证明,请参阅流行JSON库的前几段:

虽然这是一个受控异常,但它很少是可恢复的。大多数调用方应该简单地将此异常包装在未检查的异常中并重新抛出:"

那么,如果我们应该“简单地包装它”,为什么世界上有人会让开发人员不断检查异常呢?哈哈

http://developer.android.com/reference/org/json/JSONException.html


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

For example let say you have some client-server application and client had made a request for some resource that couldn't be find out or for something else error some might have occurred at the server side while processing the user request then it is the duty of the server to tell the client why he couldn't get the thing he requested for,so to achieve that at server side, code is written to throw the exception using throw keyword instead of swallowing or handling it.if server handles it/swallow it, then there will be no chance of intimating to the client that what error had occurred.

注意:为了清楚地描述发生的错误类型,我们可以创建自己的Exception对象并将其抛出给客户端。


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.


这里有一个简单的规则可以帮助你做出决定。它与Java中如何使用接口有关。

以你的类为例,想象一下为它设计一个接口,该接口描述类的功能,但不描述底层实现(接口应该这样)。假设您可能以另一种方式实现该类。

查看接口的方法,并考虑它们可能抛出的异常:

如果方法可以抛出异常,而不管底层实现是什么(换句话说,它只描述功能),那么它可能应该是接口中的受控异常。

如果异常是由底层实现引起的,那么它不应该出现在接口中。因此,它必须是类中的未检查异常(因为未检查异常不需要出现在接口签名中),或者必须将其包装并作为接口方法的一部分的已检查异常重新抛出。

To decide if you should wrap and rethrow, you should again consider whether it makes sense for a user of the interface to have to handle the exception condition immediately, or the exception is so general that there is nothing you can do about it and it should propagate up the stack. Does the wrapped exception make sense when expressed as functionality of the new interface you are defining or is it just a carrier for a bag of possible error conditions that could also happen to other methods? If the former, it might still be a checked exception, otherwise it should be unchecked.

通常不应该计划“冒泡”异常(捕获并重新抛出)。异常要么应该由调用者处理(在这种情况下它被检查),要么应该一直向上到高级处理程序(在这种情况下,如果它不被检查是最简单的)。


受控异常在编译时由JVM检查,并与资源相关(文件/db/流/套接字等)。受控异常的动机是,在编译时,如果资源不可用,应用程序应该在catch/finally块中定义一个替代行为来处理这个异常。

未经检查的异常纯粹是编程错误、错误计算、空数据甚至业务逻辑中的失败都可能导致运行时异常。在代码中处理/捕获未检查的异常是绝对没问题的。

解释摘自http://coder2design.com/java-interview-questions/


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

小薇。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)

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


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

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

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

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


我认为受控异常对于使用外部库的开发人员是一个很好的提醒,在异常情况下,该库中的代码可能会出错。


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

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的其他实现造成严重破坏!

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

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


需要指出的是,如果在代码中抛出一个受控异常,而catch在上面几层,则需要在您和catch之间的每个方法的签名中声明异常。因此,封装被破坏了,因为抛出路径中的所有函数都必须知道该异常的详细信息。


运行时异常: 运行时异常被称为未检查的异常。所有其他例外 是受控异常,并且它们不是派生自java.lang.RuntimeException。

检查异常: 必须在代码中的某个地方捕获受控异常。如果您调用 方法,该方法引发已检查异常,但您没有捕获该已检查异常 在某些地方,您的代码将无法编译。这就是为什么他们被称为检查 异常:编译器检查以确保它们被处理或声明。

Java API中的许多方法都会抛出检查过的异常,因此您将经常编写异常处理程序来处理由您没有编写的方法生成的异常。


检查异常:

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


所有异常都必须是检查异常。

Unchecked exceptions are unrestricted gotos. And unrestricted gotos are considered a bad thing. Unchecked exceptions break encapsulation. To process them correctly, all the functions in the call tree between the thrower and the catcher must be known to avoid bugs. Exceptions are errors in the function that throws them but not errors in the function that processes them. The purpose of exceptions is to give the program a second chance by deferring the decision of whether it's an error or not to another context. It's only in the other context can the correct decision be made.


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

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