首页 文章

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

提问于
浏览
611

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

Joshua Bloch在“Effective Java”中说过

对可恢复条件使用已检查的异常,对编程错误使用运行时异常(第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. Is the above consider a checked exception?

2. Is RuntimeException an unchecked exception?

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

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. Now, couldnt the above code also be a checked exception? I can try to recover the situation like this? Can I? (注意:我的第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. Why do people do this?

public void someMethod throws Exception{

}

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

EDIT: Should I bubble up the exact exception or mask it using Exception?

Below are my readings

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 回答

  • 9

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

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

    这里的异常是由于数据不良而无法在编译期间确定 .

  • 17

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

    每当涉及到已检查的异常时,方法签名中的某个地方都会出现 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");
        }
    }
    

    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赢了't compile. As InterruptedException is a checked exception, you must either capture it (with a try-catch inside method foo()) or declare that you'重新抛出它(将 throws InterruptedException 添加到方法签名中) . 因为我没有改变签名 .

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

    这个类Bar赢了't compile either! Bar'方法foo()不会覆盖IFoo的方法foo(),因为它们的签名不同 . 我可以删除@Override注释,但是我想对接口IFoo进行编程,如 IFoo foo; ,稍后决定我要使用哪个实现,如 foo = new Bar(); . 如果Bar 's method foo() doesn' t覆盖IFoo的方法foo,当我执行 foo.foo(); 时它赢得了foo()的't call Bar'实现 .

    要使Bar的 public void foo() throws InterruptedException 覆盖IFoo的 public void foo() 我必须添加 throws InterruptedException 到IFoo 's method signature. This, however, will cause problems with my Foo class, since it' s foo()方法's signature differs from IFoo'方法签名 . 此外,如果我将 throws InterruptedException 添加到Foo 's method foo() I would get another error stating that Foo'方法,则foo()声明它会抛出InterruptedException,但它永远不会抛出InterruptedException .

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

    这是检查异常为BAD的一个重要原因 . 在帽子里 .

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

  • -1

    Runtime Exceptions 运行时异常称为未经检查的异常 . 所有其他异常都是经过检查的异常,并且它们不是从java.lang.RuntimeException派生的 .

    Checked Exceptions 必须在代码中的某处捕获已检查的异常 . 如果您调用一个抛出已检查异常的方法,但您没有't catch the checked exception somewhere, your code will not compile. That'为什么要重新处理或声明它们 .

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

  • 3

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

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

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

    不,总是声明可能的最精确的异常,或者这样的列表 . 您声明方法能够抛出的异常是方法和调用者之间的 Contract 的一部分 . 抛出"FileNotFoundException"意味着文件名可能无效并且找不到该文件;调用者需要智能地处理它 . 抛出"Exception"意味着"Hey, sh*t happens. Deal."这是一个非常差的API .

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

  • 212
    • Java区分两类异常(已选中和未选中)

    • Java对已检查的异常强制执行catch或声明的要求

    • 异常的类型确定是选中还是取消选中异常 .

    • 所有作为RuntimeException类的直接或间接子类的异常类型都是未经检查的异常 .

    • 从类Exception但不是RuntimeException继承的所有类都被视为已检查的异常 .

    • 从类Error继承的类被视为未选中 .

    • 编译器检查每个方法的调用和减速,以确定该方法是否抛出已检查的异常 .

    • 如果是这样,编译器会确保在throws子句中捕获或声明异常 .

    • 为了满足catch-or-declare要求的声明部分,生成异常的方法必须提供包含checked-exception的throws子句 .

    • Exception classes are defined to be checked when they are considered important enough to catch or declare.

  • 2

    必须检查所有例外异常 .

    • 未经检查的例外是不受限制的 . 并且不受限制的getos被认为是一件坏事 .

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

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

  • 1

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

    “尽管这是一个经过检查的异常,但它很少可以恢复 . 大多数调用者只需将此异常包装在未经检查的异常中并重新抛出:”

    那么为什么世界上有人会让开发人员继续检查异常,如果我们应该“简单地换行”呢?大声笑

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

  • 2

    是否某事是"checked exception"与你是 grab 它还是在catch块中做什么无关 . 它是异常类的属性 . 除了 RuntimeException 及其子类之外, Exception 的子类的任何内容都是经过检查的异常 .

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

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

    因为这是例外的全部要点 . 没有这种可能性,您就不需要例外 . 它们使您能够在您选择的级别处理错误,而不是强迫您在最初发生的低级方法中处理它们 .

  • 0

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

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

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

  • 2

    Why do they let the exception bubble up? Isn't handling the error sooner better? Why bubble up?

    例如,假设您有一些 client-server application 并且客户端已经请求某些资源无法获取他请求的内容,因此要在服务器端实现此目的,编写代码以使用 throw 关键字抛出异常而不是吞咽或处理它 . 如果服务器处理它/吞下它,那么就没有机会向客户暗示发生了什么错误 .

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

  • 2

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

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

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

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

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

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

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

  • 7

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

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

    2 . 是的,以及扩展它的每个例外 .

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

    4 . FileNotFoundException已经是一个已检查的异常 .

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

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

    为什么设计者决定强制一个方法来指定可以在其范围内抛出的所有未捕获的已检查异常?方法抛出的任何异常都是方法的公共编程接口的一部分 . 那些调用方法的人必须知道方法可以抛出的异常,以便他们可以决定如何处理它们 . 这些异常与该方法的编程接口的参数和返回值一样多 . 接下来的问题可能是:“如果记录方法的API非常好,包括它可以抛出的异常,为什么不指定运行时异常呢?”运行时异常表示编程问题导致的问题,因此,无法合理地期望API客户端代码从它们恢复或以任何方式处理它们 . 这些问题包括算术异常,例如除以零;指针异常,例如尝试通过空引用访问对象;索引异常,例如尝试通过索引太大或太小来访问数组元素 .

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

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

    底线恕我直言,你可以 grab 任何 RuntimeException ,但你不需要和,实际上不需要实现维护相同的非检查异常,因为那些不是 Contract 的一部分 .

  • 1

    简而言之,上面的模块或模块在运行时应该处理的异常称为检查异常,其他异常是未检查的异常,它们是 RuntimeExceptionError . 在此视频中,它解释了java中的已检查和未检查的异常 . https://www.youtube.com/watch?v=ue2pOqLaArw

  • 1
    • 以上是否考虑过检查异常?否如果您正在处理异常,那么如果它是RuntimeException,则不会使其成为Checked Exception .

    • RuntimeException是未经检查的异常吗?是

    Checked Exceptions是java.lang.Exception的子类Unchecked Exceptions是java.lang.RuntimeException的子类

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

    希望这可以帮助 .

    问:我应该冒泡确切的异常还是使用异常掩盖它?

    答:是的,这是一个非常好的问题和重要的设计考虑因素 . Exception类是一个非常通用的异常类,可用于包装内部低级异常 . 您最好创建一个自定义异常并将其包装在其中 . 但是,还有一个重大问题 - 永远不要掩盖潜在的原始根本原因 . 对于前, Dont 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

    作为一般规则的RuntimeExceptions不应该是try-catched . 它们通常表示编程错误,应该保持不变 . 相反,程序员应该在调用可能导致RuntimeException的某些代码之前检查错误情况 . 例如:

    try {
        setStatusMessage("Hello Mr. " + userObject.getName() + ", Welome 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()方法中的所有字符串到整数转换代码 - 并且如果由开发人员实现则容易出错 . 所以最好不要试试try-catch .

    因此NullPointerException和NumberFormatException都是RuntimeExceptions,捕获NullPointerException应该替换为正常的空检查,而我建议显式捕获NumberFormatException以避免可能引入容易出错的代码 .

  • 63

    许多人说不应该使用经过检查的例外(即你应该明确地捕获或重新抛出的那些) . 例如,它们在C#中被淘汰,大多数语言都没有它们 . 所以你总是可以抛出 RuntimeException 的子类(未经检查的异常)

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

    Here's my extended view on the topic .

    至于具体问题:

    • Is the NumberFormatException consider a checked exception?
      编号 NumberFormatException 未选中(=是 RuntimeException 的子类) . 为什么?我不知道 . (但应该有一个方法 isValidInteger(..)

    • Is RuntimeException an unchecked exception?
      对,就是这样 .

    • What should I do here?
      这取决于此代码的位置以及您希望发生的内容 . 如果它在UI层中 - 捕获它并显示警告;如果它完全 grab 它 - 让它冒泡 . 只是不要吞下这个例外 . 如果在大多数情况下发生异常,您应该选择以下其中一种:

    • 记录并返回

    • 重新抛出它(声明它被方法抛出)

    • 通过在构造函数中传递当前的异常来构造一个新的异常

    • Now, couldn't the above code also be a checked exception? I can try to recover the situation like this? Can I?
      它可能是 . 但是没有什么可以阻止你捕获未经检查的异常

    • Why do people add class Exception in the throws clause?
      大多数情况下,因为人们懒得考虑要捕捉什么和重新抛出什么 . 投掷 Exception 是一种不好的做法,应该避免 .

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

  • 1

    Checked Exceptions

    • 例外情况由编译器检查以在运行时顺利执行程序称为Checked Exception .

    • 这些在编译时发生 .

    • 如果处理不当,它们将给出编译时错误(非异常) .

    • 除RuntimeException外,Exception类的所有子类都是Checked Exception .

    Hypothetical Example - 假设您要离开家去参加考试,但是如果您检查是否在家里(编译时)拿到了您的Hall Ticket,那么考试大厅(运行时)就不会出现任何问题 .

    Unchecked Exception

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

    • 这些在运行时发生 .

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

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

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

  • 0

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

  • 2

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

    在此处阅读有关已检查与未检查的例外的更多信息http://learnjava.today/2015/11/checked-vs-unchecked-exceptions/

  • 5

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

  • 404

    1)否,NumberFormatException是未经检查的异常 . 即使你 grab 它(你不是必须的),它也是未经检查的 . 这是因为它是IllegalArgumentException的子类,它是RuntimeException的子类 .

    2)RuntimeException是所有未经检查的异常的根 . 取消选中RuntimeException的每个子类 . 除错误(属于Throwable)之外,将检查所有其他异常和Throwables .

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

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

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

  • 0

    我最喜欢的关于unchecked和checked异常之间区别的描述是由Java Tutorial专题文章“Unchecked Exceptions - the Controversy”提供的(很抱歉在这篇文章中得到所有基础知识 - 但是,嘿,基础知识有时是最好的):

    这是底线指南:如果可以合理地期望客户端从异常中恢复,则将其作为检查异常 . 如果客户端无法执行任何操作以从异常中恢复,请将其设置为未经检查的异常

    “扔什么类型的异常”的核心是语义(在某种程度上)和上面的引用提供了很好的指导(因此,我仍然被C#摆脱检查异常的概念所震惊 - 特别是Liskov认为他们的用处) .

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

相关问题