了解 Java 中的已检查与未检查异常

Posted

技术标签:

【中文标题】了解 Java 中的已检查与未检查异常【英文标题】:Understanding checked vs unchecked exceptions in Java 【发布时间】:2011-09-01 05:37:57 【问题描述】:

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.以上是否被视为已检查异常?

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.我应该冒泡确切的异常还是使用异常来掩盖它?

以下是我的读数

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

When to choose checked and unchecked exceptions

【问题讨论】:

我有一个很好的未检查异常示例。我有一个DataSeries 类,它保存必须始终按时间顺序排列的数据。有一种方法可以将新的DataPoint 添加到DataSeries 的末尾。如果我的所有代码在整个项目中都可以正常工作,那么永远不要将DataPoint 添加到末尾,因为它的日期早于已经在末尾的那个。整个项目中的每个模块都是按照这个道理构建的。但是,我检查此条件并在发生时抛出未经检查的异常。为什么?如果发生这种情况,我想知道是谁在做这件事并修复它。 增加更多混乱。大约 10 年前,很多人都在提倡检查异常,但如今的观点越来越倾向于“检查异常是不好的”。 (但我不同意) 只有当你有一些有用的事情来处理异常时它才有用,否则你应该让调用者处理它。记录它并假装它没有发生通常是没有用的。只是重新扔它是没有意义的。包装 RuntimeException 并不像某些人想象的那么有用,它只会让编译器停止帮助你。 (恕我直言) 我们应该停止使用具有全面误导性的 checked/unchecked 异常术语。它们应该被称为 check-mandated vs check-not-mandated 例外。 我也想过第 5 点 public void method_name throws Exception 为什么有些人会这样做? 【参考方案1】:

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

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

Here's my extended view on the topic.

至于具体问题:

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

    RuntimeException 是未经检查的异常吗? 是的,没错。

    我应该在这里做什么? 这取决于此代码在哪里以及您希望发生什么。如果它在 UI 层 - 抓住它并显示警告;如果它在服务层中 - 根本不要抓住它 - 让它冒泡。只是不要吞下例外。如果在大多数情况下发生异常,您应该选择以下之一:

    记录并返回 重新抛出(声明由方法抛出) 通过在构造函数中传递当前异常来构造一个新异常

    现在,上面的代码不能也是检查异常吗?我可以尝试恢复这种情况吗?我可以吗? 本来可以的。但也没有什么能阻止你捕捉未经检查的异常

    为什么人们在 throws 子句中添加 Exception 类? 最常见的原因是人们懒于考虑捕捉什么以及重新抛出什么。投掷Exception 是一种不好的做法,应该避免。

唉,没有单一的规则可以让您确定何时捕获、何时重新抛出、何时使用已检查的异常以及何时使用未检查的异常。我同意这会导致很多混乱和很多糟糕的代码。 Bloch 陈述了一般原则(您引用了其中的一部分)。并且一般原则是在可以处理的层重新抛出异常。

【讨论】:

关于抛出异常,并不总是因为人们懒惰,你在实现框架时,让框架的用户能够抛出任何异常也是很常见的。你可以例如检查 JSE 中 Callable 接口的签名 @Kaj - 是的,诸如可调用、拦截器之类的一般事物是特殊情况。但在大多数情况下,这是因为人们很懒:) re: 3.1 “记录并返回” 明智地这样做。这非常接近于吃或藏和异常。我会为不表示问题的事情这样做,这并不是真正的例外。日志很容易被淹没和忽略。 “当你想强迫你的 API 用户思考如何处理异常情况时”——你不能强迫任何人去思考,如果他们不想。如果他们不想思考,他们将编写一个不做任何事情的糟糕异常块,或者更糟的是,删除或干扰关键错误信息。这就是为什么检查的异常是失败的。 @adrianos "......如果他们不想......你不能强迫任何人去思考......" 有了这个思路,我们也可以消除编译错误......我不是真的针对你,我一次又一次地听到这个论点,仍然发现它是标记检查异常为失败的最糟糕的解释。作为旁注,我以前见过这样一种语言,其中编译(实际上也包括运行时错误)通过设计实际上是不可能的。那条路通向一些非常黑暗的地方。【参考方案2】:

某事是否是“已检查异常”与您是否捕获它或您在 catch 块中所做的事情无关。它是异常类的属性。任何属于Exception 子类除了RuntimeException 及其子类都是检查异常。

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

他们为什么让异常冒泡 向上?是不是越早处理错误 更好的?为什么要冒泡?

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

【讨论】:

谢谢!由于垃圾中的垃圾原则,我偶尔会从我的方法中抛出异常。我团队中的一位开发人员想要输入一个无效的 xpath 表达式,由他们来处理异常。万一他们捕捉到异常并且什么都不做,他们会在代码审查中听到。 “除了 RuntimeException 及其子类之外的 Throwable 的任何子类都是检查异常。” - 你的说法不正确。 Error 也继承了 Throwable 并且是未选中的。 @JonasEicher:基本上,异常的一个主要优点是它们允许您选择要在调用堆栈中处理错误的位置,这通常非常高,同时保持中间层完全免费错误处理工件。受检查的异常恰恰破坏了这种优势。另一个问题是已检查/未检查的区别与异常类相关联,该类也表示异常的概念分类 - 混合了根本不一定相关的两个方面。 “但大多数人的意见似乎是,它造成的设计问题不值得。” - 请引用? @Bartzilla 是的。为了完整起见,正如 Throwable 的 javadoc 所说:“Throwable 和任何不是 RuntimeException 或 Error 子类的 Throwable 子类都被视为已检查异常”【参考方案3】:

    以上是否被视为已检查异常? 不 如果它是 RuntimeException,则您正在处理异常这一事实不会使其成为 Checked Exception

    RuntimeExceptionunchecked exception 吗? 是的

Checked Exceptionssubclassesjava.lang.Exception Unchecked Exceptionssubclassesjava.lang.RuntimeException

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

希望这会有所帮助。

问:我应该准确地冒泡吗? 异常还是使用 Exception 屏蔽它?

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

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

但有时这种错误检查成本很高,例如数字格式,请考虑一下 -

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。

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

【讨论】:

谢谢。当您冒泡exception 时,还有一个问题,我应该冒泡确切的异常还是使用Exception 掩盖它。我在一些遗留代码之上编写代码,Exception 到处冒泡。我想知道这是否是正确的行为? 这是一个非常好的和重要的问题,编辑了我的答案以包含解释。 非常感谢。可以给我看一下LoginFailureException(sqle)的内容吗? 我没有任何代码,我只是编造了名称等。如果你看到 java.lang.Exception,它有 4 个构造函数,其中两个接受 java.lang.Throwable。在上面的 sn-ps 中,我假设 LoginFailureException 扩展了 Exception 并声明了一个构造函数 public LoginFailureException(Throwable cause) super(cause) 关于该主题的最佳答案。我认为不应该捕获运行时异常,因为这些异常是由于缺乏良好的编程而发生的。我完全同意“消除最初的根本原因会掩盖无法恢复的实际原因对于生产支持团队来说是一场噩梦,他们只能访问应用程序日志和错误消息。”。【参考方案4】:

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 文档本身是一个很好的阅读材料:http://download.oracle.com/javase/tutorial/essential/exceptions/runtime.html

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

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

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

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

恕我直言,底线是您可以捕获任何RuntimeException,但您不需要,实际上不需要实现来维护抛出的相同的非检查异常,如这些不是合同的一部分。

【讨论】:

谢谢。当您冒泡exception 时,还有一个问题,我应该冒泡确切的异常还是使用Exception 掩盖它。我在一些遗留代码之上编写代码,Exception 到处冒泡。我想知道这是否是正确的行为? @Harry 我会让比我更有知识的人来回答这个问题:***.com/questions/409563/…【参考方案5】:

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

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

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

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

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

【讨论】:

谢谢。当您冒泡exception 时,还有一个问题,我应该冒泡确切的异常还是使用Exception 掩盖它。我在一些遗留代码之上编写代码,Exception 到处冒泡。我想知道这是否是正确的行为? 您可以让您的方法也抛出异常(这并不理想)。或者捕获异常并抛出更好的异常(比如 IOException 什么的)。所有异常都可以将其构造函数中的异常作为“原因”,因此您应该使用它。【参考方案6】:

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

例如..文件操作

未检查 - 由于数据错误。在运行时检查。

例如..

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)

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

【讨论】:

【参考方案7】:

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

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

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

【讨论】:

【参考方案8】:

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

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

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

【讨论】:

【参考方案9】:

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

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

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

不,总是声明可能的最精确异常,或者这样的列表。你声明你的方法能够抛出的异常是你的方法和调用者之间契约的一部分。抛出"FileNotFoundException" 表示文件名可能无效,找不到文件;调用者需要智能地处理它。投掷Exception 的意思是“嘿,sh*t 发生了。成交。”这是一个很可怜的API

在第一篇文章的 cmets 中有一些示例,其中“throws Exception”是一个有效且合理的声明,但对于您将编写的大多数“normal”代码而言,情况并非如此。

【讨论】:

没错,让您的检查异常声明成为您代码文档的一部分,并帮助使用您的软件的人。【参考方案10】:

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

【讨论】:

【参考方案11】:

我的绝对喜欢对未检查异常和检查异常之间区别的描述由 Java 教程跟踪文章“Unchecked Exceptions - the Controversy”提供(很抱歉在这篇文章中获得了所有基本信息 - 但是,嘿,基础知识有时是最好的):

以下是底线准则:如果客户可以合理地 期望从异常中恢复,使其成为受检异常。如果 客户端无法从异常中恢复,使其成为 未经检查的异常

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

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

【讨论】:

【参考方案12】:

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

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

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

【讨论】:

好点。这意味着将其提升到最负责的最高层,该层控制逻辑流并监督应用程序的业务逻辑。例如,数据库层不可能向客户端传达关键信息丢失或无响应的信息。当它冒泡到最顶层的服务器层时,它会直接刷新客户端的视图并显示严重错误消息。【参考方案13】:

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

每当涉及到检查异常时,方法签名中的某处都会有一个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 类非常好。现在让我们第一次尝试 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)或声明您正在抛出它(将 throws 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() 实现。

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

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

这是检查异常不好的一大原因。大写。

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

【讨论】:

是的,这很糟糕,因为你说你不想抓住它。但是为了防止影响 IFOO 的签名,您必须这样做。我宁愿这样做并继续前进,而不是重新平衡我所有的接口签名以避免捕获异常(仅仅因为异常是坏的)。 是的,我同意。我有点不清楚。我想要传播一个异常,所以我可以在其他地方处理它。一种解决方案是捕获 InterruptedException 并抛出未经检查的异常。即我们避免检查异常并传递未经检查的异常(即使它们仅作为包装有意义) “但是,这会导致我的 Foo 类出现问题,因为它的 foo() 方法的签名与 IFoo 的方法签名不同”。我刚刚测试了你的想法,即使我们在 IFoo 的方法签名中添加throws InterruptedException 也可以编译,而不会在任何实现中抛出任何东西。所以它并没有真正引起任何问题。如果在接口中你让每个方法签名都抛出Exception,它只是让实现选择抛出或不抛出异常(任何异常,因为Exception 封装了所有异常)。 但是我承认这可能会造成混淆,因为当您实现这样的接口并单击“添加未实现的方法”之类的内容时,它们将自动创建,并在其签名中使用 throw Exception 子句,即使您的实现不会抛出任何东西或者可能是更具体的异常。但我仍然觉得总是为接口的方法抛出异常是一个好习惯,因为它再次让用户选择抛出或不抛出任何东西。 这没有抓住重点。接口的目的是声明客户需要满足的合同。这可能包括它能够处理的故障场景。当实现遇到错误时,它应该将该错误映射到客户端接口声明的适当抽象故障。【参考方案14】: Java 区分两类异常(已检查和未检查)。 Java 对已检查的异常强制执行捕获或声明要求。 异常的类型决定了异常是选中还是未选中。 类RuntimeException 的直接或间接subclasses 的所有异常类型 是未经检查的异常。 所有继承自类Exception 但不是RuntimeException 的类都被视为checked exceptions。 从类 Error 继承的类被认为是未选中的。 编译器检查每个方法调用和减速以确定是否 方法抛出checked exception。 如果是这样,编译器会确保异常被捕获或在 throws 子句中声明。 为了满足 catch-or-declare 要求的声明部分,生成的方法 异常必须提供包含checked-exceptionthrows 子句。 Exception 类被定义为在它们被认为足够重要以捕获或声明时进行检查。

【讨论】:

【参考方案15】:

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

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

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

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

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

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

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

【讨论】:

【参考方案16】:

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

【讨论】:

【参考方案17】:

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

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

【讨论】:

【参考方案18】:

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

【讨论】:

嗯。如果 NumberFormatException 是一个已检查的异常,就像你说的那样,这不是与它是 inherited from RuntimeException 的事实相矛盾吗? 对不起,我不是很清楚。我指的是 FileNotFoundException 而不是 NumberFormatException。根据他的 #2 和 #4,他似乎认为 Checked 与 Unchecked 是基于您在捕获异常后如何处理异常。不是它的定义方式。【参考方案19】:

检查异常

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

这些发生在编译时。

如果这些处理不当,就会出现编译时错误(Not Exception)。

Exception类的所有子类除了RuntimeException都是Checked Exception。

假设示例 - 假设您要离开家去参加考试,但如果您检查您是否在家中拿了 Hall Ticket(编译时),那么不会有'在考场(运行时)不会有任何问题。

未经检查的异常

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

这些发生在运行时。

如果这些异常处理不当,它们不会产生编译时错误。但是程序会在运行时提前终止。

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

假设示例 - 假设您在考场,但不知何故您的学校发生了火灾事故(意味着在运行时),当时您无能为力但可以在(编译时)之前做好预防措施。

【讨论】:

【参考方案20】:

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

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

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

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

【讨论】:

因为只有大多数调用者,而不是所有调用者,应该换行并重新抛出。检查异常这一事实意味着调用者必须考虑他们是“最多”的调用者之一,还是能够并且应该处理异常的少数调用者之一。 如果您喜欢为每次调用检查错误,请“返回”到 C。异常是一种将正常程序执行与异常分开的方法,不会污染您的代码。异常确保您不能在在某个级别默默地忽略错误。【参考方案21】:

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

    未经检查的异常是不受限制的 goto。而且不受限制的 goto 被认为是一件坏事。

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

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

【讨论】:

以上是关于了解 Java 中的已检查与未检查异常的主要内容,如果未能解决你的问题,请参考以下文章

您如何判断是不是使异常检查或未检查?

处理 lambda 中的已检查异常 [重复]

如何处理 lambda 中的已检查异常? [复制]

从 lambda 表达式中抛出的已检查异常

检查对象属性时与未定义或“未定义”的比较。有什么不同?

如何在c#中只打印datagridview中的已检查行