Java:已检查vs未经检查的异常说明

时间:2011-05-24 19:38:32

标签: java exception runtimeexception checked-exceptions unchecked-exception

我已经在StackOverFlow上阅读了有关已检查和未经检查的异常的多个帖子。老实说,我还是不太确定如何正确使用它们。

Effective Java ”中的Joshua Bloch说

  

使用已检查的例外   可恢复的条件和运行时间   编程错误的例外   (第2版第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。现在,上述代码也不能成为检查异常吗?我可以尝试恢复这样的情况吗?我可以吗?(注意:我的第3个问题在上面的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{

}

为什么他们让异常泡沫破灭?是不是更快地处理错误?为什么泡起来?

编辑:我应该冒泡确切的异常还是使用异常屏蔽它?

以下是我的阅读材料

In Java, when should I create a checked exception, and when should it be a runtime exception?

When to choose checked and unchecked exceptions

21 个答案:

答案 0 :(得分:435)

许多人都说不应该使用经过检查的例外(即应明确捕获或重新抛出的例外)。例如,它们在C#中被淘汰,大多数语言都没有它们。因此,您始终可以抛出RuntimeException(未经检查的异常)

的子类

但是,我认为已检查的异常很有用 - 当您希望强制API的用户考虑如何处理异常情况(如果它是可恢复的)时,会使用它们。只是被检查的异常在Java平台中被过度使用,这让人们讨厌它们。

Here's my extended view on the topic

至于具体问题:

  1. NumberFormatException是否考虑经过检查的例外?
    不。NumberFormatException未选中(=是RuntimeException的子类)。为什么?我不知道。 (但应该有方法isValidInteger(..)

  2. RuntimeException是未经检查的例外吗?
    是的,完全正确。

  3. 我该怎么办?
    这取决于此代码的位置以及您希望发生的内容。如果它在UI层中 - 捕获它并显示警告;如果它在服务层 - 根本不抓住它 - 让它冒泡。只是不要吞下这个例外。如果在大多数情况下发生异常,您应该选择以下其中一种:

    • 记录并返回
    • 重新抛出它(声明它被方法抛出)
    • 通过在构造函数
    • 中传递当前的异常来构造一个新的异常
  4. 现在,上述代码难道也不是一个经过检查的例外吗?我可以尝试恢复这样的情况吗?我可以吗?
    它可能是。但是没有什么可以阻止你捕获未经检查的异常

  5. 为什么人们会在throws子句中添加类Exception
    大多数情况下,因为人们懒得考虑要捕捉什么和重新抛出什么。投掷Exception是一种不好的做法,应该避免。

  6. 唉,没有一条规则可以让您确定何时捕获,何时重新抛出,何时使用已检查以及何时使用未经检查的异常。我同意这会引起很多混乱和很多不好的代码。 Bloch说明了一般原则(你引用了它的一部分)。一般原则是重新抛出可以处理它的图层的异常。

答案 1 :(得分:220)

某些内容是否为“已检查的异常”与您是否捕获它或在catch块中执行的操作无关。它是异常类的属性。除了Exception及其子类的之外的任何RuntimeException 的子类都是一个经过检查的异常。

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

  

为什么他们让异常泡沫化   吗?不会错误处理错误   更好?为什么要冒泡?

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

答案 2 :(得分:64)

  1. 以上是否被视为已检查的例外? 没有 您处理例外的事实如果是Checked Exception,则不会使其成为RuntimeException

  2. RuntimeExceptionunchecked exception吗? 是

  3. Checked Exceptionssubclasses的{​​{1}} java.lang.ExceptionUnchecked Exceptions

    subclasses

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

    希望这有帮助。

      问:我应该确切地说出来   异常或使用异常掩盖它?

    答:是的,这是一个非常好的问题和重要的设计考虑因素。 Exception类是一个非常通用的异常类,可用于包装内部低级异常。您最好创建一个自定义异常并将其包装在其中。但是,也是一个重大问题 - 永远不要掩盖潜在的原始根本原因。例如,java.lang.RuntimeException执行以下操作 -

    Don't ever

    取而代之的是:

    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!. } 返回是否包含在任何特定于应用程序的异常中。

      

    尝试捕捉Always pass on the actual exception

    RuntimeExceptions作为一般规则不应该被试图捕获。它们通常表示编程错误,应该保持不变。相反,程序员应该在调用可能导致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.");
    }
    

    但有时候这种错误检查很昂贵,例如数字格式化,请考虑这个 -

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

    这里预调用错误检查不值得付出努力,因为它本质上意味着复制parseInt()方法中的所有字符串到整数转换代码 - 并且如果由开发人员实现则容易出错。所以最好不要试试try-catch。

    所以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."); } NullPointerException都是NumberFormatException,抓住RuntimeExceptions应该替换为优雅的空检查,而我建议明确地抓住NullPointerException避免可能引入容易出错的代码。

答案 3 :(得分:18)

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 已经是一个已检查的异常。

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

好的阅读是Oracle doc本身:http://download.oracle.com/javase/tutorial/essential/exceptions/runtime.html

  

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

     

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

Java Language Specification中还有一些重要的信息:

  

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

底线恕我直言,你可以捕获任何RuntimeException,但你不需要,实际上不需要实现来保持抛出相同的非检查异常,因为那些不是合同的一部分。

答案 4 :(得分:9)

1)不,NumberFormatException是未经检查的异常。即使你抓住它(你不是必须的)因为它没有被检查。这是因为它是IllegalArgumentException的子类,它是RuntimeException的子类。

2)RuntimeException是所有未经检查的例外的根。 RuntimeException的每个子类都未选中。检查所有其他例外和Throwable,但错误除外(Throwable下)。

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

5)投掷和捕捉'Exception'是不好的做法。但更一般地说,您可能会抛出其他异常,以便调用者可以决定如何处理它。例如,如果您编写了一个库来处理读取某些文件输入并且您的方法传递了一个不存在的文件,那么您根本不知道如何处理它。来电者是想再次询问还是退出?所以你将Exception向上链回到调用者。

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

答案 5 :(得分:7)

检查 - 容易发生。签入编译时间。

例如.. FileOperations

UnChecked - 由于数据不佳。检查运行时间。

例如..

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)

此异常是由于数据错误造成的,在编译期间无法确定。

答案 6 :(得分:5)

JVM在编译时检查已检查的异常,并将其与资源(文件/ db / stream / socket等)相关联。检查异常的动机是在编译时如果资源不可用,应用程序应该定义一个替代行为来在catch / finally块中处理它。

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

来自http://coder2design.com/java-interview-questions/

的解释

答案 7 :(得分:3)

运行时例外 运行时异常称为未经检查的异常。所有其他例外 检查异常,它们不是从java.lang.RuntimeException派生的。

已检查的例外情况 必须在代码中的某处捕获已检查的异常。如果你调用一个 抛出已检查异常但未捕获已检查异常的方法 在某个地方,你的代码将无法编译。这就是他们被调查的原因 例外:编译器检查以确保它们被处理或声明。

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

答案 8 :(得分:3)

我的绝对喜欢的描述了未经检查和检查的异常之间的区别,这是由Java Tutorial专题文章提供的,&#34; Unchecked Exceptions - the Controversy&#34; (很抱歉在这篇文章中得到所有的基础 - 但是,嘿,基础知识有时是最好的):

  

以下是底线指南:如果客户可以合理地做到   期望从异常中恢复,使其成为检查异常。如果   客户端无法做任何事情从异常中恢复,使其成为一个   未经检查的异常

&#34的核心是什么类型的异常抛出&#34;是语义(在某种程度上)和上面的引用提供和优秀的准则(因此,我仍然被C#摆脱检查异常的概念所震惊 - 特别是Liskov认为它们的用处)。

其余部分变得合乎逻辑:编译器希望我明确地响应哪些异常?您希望客户从中恢复的那些。

答案 9 :(得分:2)

要回答最后一个问题(其他人似乎在上面已经完全回答),“我应该冒泡确切的异常还是使用异常掩盖它?”

我假设你的意思是这样的:

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

不,始终声明最精确的异常,或者列出此类异常。您声明您的方法能够抛出的例外是您的方法和调用者之间的合同的一部分。抛出"FileNotFoundException"意味着文件名可能无效并且找不到文件;调用者需要智能地处理它。投掷Exception意味着“嘿,不要发生。交易。”哪个是非常差的API

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

答案 10 :(得分:2)

  • Java区分两类异常(已检查和未检查)。
  • Java对已检查的异常强制执行catch或声明的要求。
  • 异常类型确定是选中还是取消选中例外。
  • subclasses的直接或间接RuntimeException的所有异常类型 是未经检查的例外。
  • 从类Exception但不是RuntimeException继承的所有类都被视为checked exceptions
  • 继承自类Error的类被视为未选中。
  • 编译器检查每个方法的调用和减速以确定是否 方法抛出checked exception
    • 如果是这样,编译器会确保在throws子句中捕获或声明异常。
  • 为了满足catch-or-declare要求的声明部分,生成的方法 该例外必须提供包含throws
  • checked-exception子句
  • Exception类被定义为在被认为足够重要以捕获或声明时进行检查。

答案 11 :(得分:2)

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

每当涉及检查过的异常时,方法签名中的某个地方throws CheckedExceptionCheckedException可能是任何已检查的异常)。签名不会抛出异常,抛出异常是实现的一个方面。接口,方法签名,父类,所有这些都不应该依赖于它们的实现。这里使用已检查的异常(实际上您必须在方法签名中声明throws)将更高级别的接口与这些接口的实现绑定。

让我举个例子。

让我们拥有一个漂亮而干净的界面

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");
    }
}

Class 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();
    }
}

这个班级栏不会编译。由于InterruptedException是一个经过检查的异常,您必须捕获它(在方法foo()中使用try-catch)或声明您正在抛出它(将throws InterruptedException添加到方法签名中)。因为我不想在这里捕获这个异常(我希望它向上传播,所以我可以在其他地方妥善处理它),让我们改变签名。

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

这个班级酒吧也不会编译! Bar的方法foo()不会覆盖IFoo的方法foo(),因为它们的签名是不同的。我可以删除@Override注释,但我想针对IFoo foo;之类的接口IFoo进行编程,然后决定我要使用哪个实现,例如foo = new Bar();。如果Bar的方法foo()没有覆盖IFoo的方法foo,那么当我foo.foo();时它不会调用Bar的foo()实现。

要使Bar public void foo() throws InterruptedException覆盖IFoo&#39; s public void foo()我必须将throws InterruptedException添加到IFoo的方法签名中。但是,这会导致我的Foo类出现问题,因为它的foo()方法的签名与IFoo的方法签名不同。此外,如果我将throws InterruptedException添加到Foo的方法foo()中,我会得到另一个错误,指出Foo的方法foo()声明它抛出InterruptedException但它从不抛出InterruptedException。 / p>

正如你所看到的(如果我在解释这些内容方面做得不错),我抛出一个像InterruptedException这样的已检查异常的事实迫使我将我的界面IFoo绑定到其中一个上面。实现,这反过来又会对IFoo的其他实现造成严重破坏!

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

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

答案 12 :(得分:1)

简而言之,上面的模块或模块在运行时应该处理的异常称为检查异常;其他是未经检查的例外,可以是RuntimeExceptionError

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

答案 13 :(得分:1)

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

答案 14 :(得分:1)

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

在此处详细了解已检查与未经检查的例外http://learnjava.today/2015/11/checked-vs-unchecked-exceptions/

答案 15 :(得分:1)

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

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

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

如果某个方法可以抛出异常,无论底层实现如何(换句话说,它仅描述了该功能),那么它应该是接口中的已检查异常。

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

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

您通常不应该计划“冒泡”异常(捕获和重新抛出)。一个异常应该由调用者处理(在这种情况下它被检查),或者它应该一直到高级处理程序(在这种情况下,如果未选中它是最容易的)。

答案 16 :(得分:1)

为什么他们让异常泡沫破灭?是不是更快地处理错误?为什么要冒泡?

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

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

答案 17 :(得分:0)

所有这些都是经过检查的例外情况。未经检查的异常是RuntimeException的子类。决定不是如何处理它们,应该是你的代码抛出它们。如果您不希望编译器告诉您尚未处理异常,则使用未经检查(RuntimeException的子类)异常。这些应该保存在你无法恢复的情况下,比如内存不足等等。

答案 18 :(得分:-1)

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

&#34;虽然这是一个经过检查的例外,但它很少可以恢复。大多数调用者只需将此异常包装在未经检查的异常中并重新抛出:&#34;

那么为什么世界上任何人都会让开发人员继续检查异常,如果我们应该&#34;简单地包装它&#34;代替?洛尔

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

答案 19 :(得分:-1)

已检查的例外情况

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

  • 这些在编译时发生。

  • 如果处理不当,则会产生编译时错误(非异常)。
  • Exception类的所有子类(RuntimeException除外)都是Checked Exception。

    假设示例 - 假设您要离开家去参加考试,但如果您检查一下是否在家里(编译时)拿到了您的Hall Ticket,那么就赢了&# 39;考试大厅(运行时)有任何问题。

未选中的例外

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

  • 这些在运行时发生。

  • 如果未正确处理这些异常,则不会给出编译时错误。但程序将在运行时提前终止。

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

    假设示例 - 假设您在考场,但不知何故,您的学校发生了火灾事故(在运行时的意思)您无法做任何事情那个时候,但可以在(编译时)之前采取预防措施。

答案 20 :(得分:-1)

必须检查所有例外情况。

  1. 未经检查的例外情况是不受限制的。并且不受限制的骗局被认为是一件坏事。

  2. 未经检查的异常会破坏封装。要正确处理它们,必须知道thrower和catcher之间的调用树中的所有函数以避免错误。

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