English 中文(简体)
理解Java中的已检查与未检查异常
原标题:Understanding checked vs unchecked exceptions in Java

Joshua Bloch在“高效Java”中说

Use checked exceptions for recoverable conditions and runtime exceptions for programming errors (Item 58 in 2nd edition)

让我们看看我是否理解正确。

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

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.现在,上面的代码不能也是一个检查异常吗?我可以试着恢复这种局面吗?我可以吗(注意:我的第三个问题在上面的catch中)

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.我应该弹出确切的异常还是使用exception屏蔽它

以下是我的阅读资料

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

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

最佳回答

许多人说,根本不应该使用已检查的异常(即,您应该明确捕获或重新抛出的异常)。例如,它们在C#中被删除了,而大多数语言都没有。因此,您可以始终抛出RuntimeException的子类(未检查的异常)

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

这是我对这个主题的扩展看法

至于具体问题:

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

  2. Is RuntimeException an unchecked exception?
    Yes, exactly.

  3. 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
  4. 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

  5. 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的及其子类之外,任何属于Exception的子类都是已检查异常。

Java编译器强制您捕获已检查的异常或在方法签名中声明它们。它本应提高程序的安全性,但大多数人似乎认为它不值得制造设计问题。

Why do they let the exception bubble up? Isnt handle error the sooner the better? Why bubble up?

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

  1. Is the above considered to be a checked exception? No The fact that you are handling an exception does not make it a Checked Exception if it is a RuntimeException.

  2. Is RuntimeException an unchecked exception? Yes

Checked Exceptions are subclasses of java.lang.Exception Unchecked Exceptions are subclasses of java.lang.RuntimeException

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

希望这能有所帮助。

Q: should I bubble up the exact exception or mask it using Exception?

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!.
}

Eating away original root cause buries the actual cause beyond recovery is a nightmare for production support teams where all they are given access to is application logs and error messages. Although the latter is a better design but many people don t use it often because developers just fail to pass on the underlying message to caller. So make a firm note: Always pass on the actual exception back whether or not wrapped in any application specific exception.

在尝试捕获RuntimeExceptions

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

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

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

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()方法中复制所有字符串到整数的转换代码,并且如果由开发人员实现,则很容易出错。所以最好不要尝试接球。

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

1.如果您对异常不确定,请检查API:

 java.lang.Object
 extended by java.lang.Throwable
  extended by java.lang.Exception
   extended by java.lang.RuntimeException  //<-NumberFormatException is a RuntimeException  
    extended by java.lang.IllegalArgumentException
     extended by java.lang.NumberFormatException

2.是的,还有每一个扩展它的例外。

3.没有必要捕获并抛出相同的异常。在这种情况下,您可以显示一个新的“文件”对话框。

4.FileNotFoundException<em>已经是检查的异常。

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

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

为什么设计人员决定强制一个方法指定其范围内可以抛出的所有未捕获的已检查异常?方法可以抛出的任何异常都是该方法公共编程接口的一部分。那些调用方法的人必须知道方法可以抛出的异常,这样他们才能决定如何处理这些异常。这些异常与其参数和返回值一样,也是该方法编程接口的一部分。

下一个问题可能是:“如果记录一个方法的API(包括它可以抛出的异常)很好,为什么不也指定运行时异常呢?”运行时异常表示由编程问题引起的问题,因此,不能合理地期望API客户端代码从中恢复或以任何方式处理它们。这类问题包括算术异常,例如除以零;指针异常,例如试图通过null引用访问对象;以及索引异常,例如试图通过过大或过小的索引访问数组元素。

Java语言规范

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

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

1) 否,NumberFormatException是未选中的Exception。即使你发现了它(你不需要),因为它没有被检查。这是因为它是IllegalArgumentException的子类,而RuntimeException是其子类。

2) <code>RuntimeException</code>是所有未检查异常的根源。<code>RuntimeException</code>的每个子类都未选中。除了错误(位于Throwable下)外,将检查所有其他异常和可抛出

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

5) 抛出并捕获<code>异常</code>是一种糟糕的做法。但更普遍的情况是,您可能会抛出其他异常,以便调用方可以决定如何处理它。例如,如果您编写了一个库来处理读取一些文件输入,而您的方法被传递了一个不存在的文件,则您不知道如何处理。来电者想再次询问还是退出?因此,您将Exception向上抛出,返回给调用者。

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

选中-很可能发生。签入编译时。

文件操作

未检查-由于数据错误。已签入运行时。

例如。。

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)

这里的异常是由于坏数据造成的,并且在编译时无法确定。

Runtime Exceptions : Runtime exceptions are referring to as unchecked exceptions. All other exceptions are checked exceptions, and they don t derive from java.lang.RuntimeException.

Checked Exceptions : A checked exception must be caught somewhere in your code. If you invoke a method that throws a checked exception but you don t catch the checked exception somewhere, your code will not compile. That s why they re called checked exceptions : the compiler checks to make sure that they re handled or declared.

Java API中的许多方法都会抛出已检查的异常,所以您经常会编写异常处理程序来处理未编写的方法所生成的异常。

被检查的异常在编译时由JVM及其相关资源(files/db/stream/socket等)进行检查。检查异常的动机是,在编译时,如果资源不可用,应用程序应该定义一种替代行为,以在catch/finaly块中处理此问题。

未检查的异常是纯粹的程序错误、错误计算、空数据,甚至业务逻辑中的故障都可能导致运行时异常。在代码中处理/捕获未检查的异常是非常好的。

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

Java教程跟踪文章“描述https://docs.oracle.com/javase/tutorial/essential/exceptions/runtime.html“rel=”noreferrer“>未检查的异常-争议”(很抱歉在这篇文章中获得了所有基本信息,但是,嘿,基本信息有时是最好的):

Here s the bottom line guideline: If a client can reasonably be expected to recover from an exception, make it a checked exception. If a client cannot do anything to recover from the exception, make it an unchecked exception

“抛出什么类型的异常”的核心是语义(在某种程度上),上面的引用提供了一个很好的指导(因此,我仍然对C#摆脱了检查异常的概念感到震惊,尤其是当Liskov认为它们有用时)。

剩下的就变得合乎逻辑了:编译器希望我明确地响应哪些异常?您希望客户端从中恢复的那些。

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

我想你的意思是这样的:

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

否,请始终声明最精确的异常,或此类异常的列表。将方法声明为能够抛出的异常是方法和调用者之间契约的一部分。抛出“FileNotFoundException”意味着文件名可能无效并且找不到文件;调用方将需要智能地处理该问题。抛出异常意味着“嘿,sh*t发生了。处理。”这是一个非常糟糕的API

在第一篇文章的评论中,有一些例子表明“throws<code>Exception</code>”是一个有效且合理的声明,但对于您将要编写的大多数“<code>normal</code>”代码来说,情况并非如此。

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

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

例如,假设您有一个客户端-服务器应用程序,并且客户端请求了一些找不到的资源,或者在处理用户请求时服务器端可能发生了其他错误,那么服务器有责任告诉客户端他为什么得不到他请求的东西,以便在服务器端实现这一点,编写代码是为了使用throw关键字抛出异常,而不是吞下或处理它。如果服务器处理它/吞下它,那么就没有机会向客户端通知发生了什么错误。

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

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

每当涉及检查异常时,在方法签名中的某个地方就会有一个<code>throws CheckedException</code>(<code>CheckedException>可以是任何检查异常)。签名不会引发异常,引发异常是实现的一个方面。接口、方法签名、父类,所有这些都不应该依赖于它们的实现。这里使用的checked Exceptions(实际上是您必须在方法签名中声明<code>throws</code>的事实)将您的高级接口与这些接口的实现绑定在一起。

让我给你举个例子。

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

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班非常好。现在让我们第一次尝试吧

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),或者声明您正在抛出它(将<code>throws Interrupted exception

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

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

要使Bar的<code>public void foo()throws InterruptedException</code>覆盖IFoo的<code>public void foo()</code>,我必须将<code>throws IntertruptedException>添加到IFoo的方法签名中。然而,这将导致我的Foo类出现问题,因为它的Foo()方法的签名与IFoo的方法签名不同。此外,如果我将<code>throws InterruptedException</code>添加到Foo的方法Foo()中,我会得到另一个错误,说明Foo的方式Foo(。

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

这是检查异常为BAD的一个重要原因。大写。

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

  • 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中如何使用接口有关。

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

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

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

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

要决定是否应该包装和重新抛出,您应该再次考虑接口的用户必须立即处理异常情况是否有意义,或者异常非常普遍,您对此无能为力,它应该向上传播。当被包装的异常被表示为您正在定义的新接口的功能时,它是否有意义,或者它只是一个可能发生在其他方法上的错误条件的载体?如果是前者,它可能仍然是一个已检查的异常,否则应该取消检查。

您通常不应该计划“冒泡”异常(catch和rethrow)。异常应该由调用方处理(在这种情况下,它被选中),或者它应该一直到高级处理程序(在那种情况下,如果不选中它,它最容易)。

只是要指出的是,如果您在代码中抛出一个已检查的异常,并且catch比它高几级,那么您需要在您和catch之间的每个方法的签名中声明该异常。因此,封装被破坏了,因为throw路径中的所有函数都必须知道该异常的详细信息。

简而言之,上面的一个或多个模块应该在运行时处理的异常称为检查异常;其他是未检查的异常,即RuntimeExceptionError

In this video, it explains checked and unchecked exceptions in Java:
https://www.youtube.com/watch?v=ue2pOqLaArw

所有这些都是已检查的异常。未选中的异常是RuntimeException的子类。决定的不是如何处理它们,而是你的代码应该抛出它们。如果您不希望编译器告诉您尚未处理异常,那么您可以使用未检查的(RuntimeException的子类)异常。这些应该保存起来,以备无法恢复的情况,比如内存不足等。

已检查异常

  • 编译器为程序在运行时顺利执行而检查的异常称为检查异常。

  • 这些都发生在编译时。

  • If these are not handled properly, they will give compile time error (Not Exception).
  • 除了RuntimeException之外,Exception类的所有子类都是Checked Exception。

    假设示例-假设你要离开家去参加考试,但如果你在家检查是否带了大厅门票(编译时),那么考场(运行时)就不会有任何问题。

未选中的异常

  • 编译器未检查的异常称为未检查异常。

  • 这些都发生在运行时。

  • 如果这些异常没有得到正确处理,它们就不会产生编译时错误。但该程序将在运行时提前终止。

  • RunTimeException和Error的所有子类都是未检查的异常。

    假设示例-假设你在考场上,但不知何故你的学校发生了火灾事故(意味着在运行时),当时你什么都做不了,但可以在(编译时)之前采取预防措施。

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

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

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

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

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

  1. 未选中的异常是不受限制的goto。不受限制的goto被认为是一件坏事。

  2. 未选中的异常会破坏封装。为了正确处理它们,必须知道抛出器和捕获器之间的调用树中的所有函数,以避免出现错误。

  3. 异常是抛出异常的函数中的错误,而不是处理异常的函数的错误。异常的目的是通过将是否是错误的决定推迟到另一个上下文来给程序第二次机会。只有在其他情况下才能做出正确的决定。





相关问题
Spring Properties File

Hi have this j2ee web application developed using spring framework. I have a problem with rendering mnessages in nihongo characters from the properties file. I tried converting the file to ascii using ...

Logging a global ID in multiple components

I have a system which contains multiple applications connected together using JMS and Spring Integration. Messages get sent along a chain of applications. [App A] -> [App B] -> [App C] We set a ...

Java Library Size

If I m given two Java Libraries in Jar format, 1 having no bells and whistles, and the other having lots of them that will mostly go unused.... my question is: How will the larger, mostly unused ...

How to get the Array Class for a given Class in Java?

I have a Class variable that holds a certain type and I need to get a variable that holds the corresponding array class. The best I could come up with is this: Class arrayOfFooClass = java.lang....

SQLite , Derby vs file system

I m working on a Java desktop application that reads and writes from/to different files. I think a better solution would be to replace the file system by a SQLite database. How hard is it to migrate ...