【问题标题】:Java exceptions rationaleJava 异常基本原理
【发布时间】:2012-02-03 14:20:05
【问题描述】:

这次是一个相当理论的问题。所以我在 Eclipse 中使用了这个函数:

CsvReader csv = new CsvReader("src/maindroite.csv");

由于“未处理的异常类型 FileNotFoundException”而无法运行。好的,我知道我必须为文件不存在的情况添加一些东西,此时我通常会添加几行来捕获异常并将其丢弃。但我的问题是:为什么我需要捕获异常即使文件确实存在?实际上,为什么我什至对某些功能而不是其他功能有这个异常?

例如,假设我正在尝试运行:

ImageIcon icon1 = new ImageIcon("src/square.jpg");
ImageIcon icon2 = new ImageIcon("src/circle.jpg");

存在“square.jpg”但不存在“circle.jpg”的位置。该程序将创建 icon1,但不会创建 icon2,因为它不能。但是我不需要为图像不存在的情况添加 ExceptionHandler 。这两个函数有什么区别?

总结一下:

  • 当文件确实存在时,为什么我必须添加一个ExceptionHandler?
  • 为什么我必须为某些函数而不是其他函数添加 ExceptionHandler?

谢谢!

【问题讨论】:

  • 因为JVM无法读懂你的想法。

标签: java exception exception-handling theory


【解决方案1】:

为什么我必须在文件确实存在时添加 ExceptionHandler?

基本上你必须添加它,因为你不能编写这样的条件代码,总之编译器无法在运行前知道文件是否存在,因此编译器强制你放置一个 try/catch 块,因为 FileNotFoundException 是一个 checked 异常。

为什么我必须为某些函数而不是其他函数添加 ExceptionHandler?

您只需将 try/catch 块添加到任何引发检查异常的东西上,即任何 **NOT* 继承自 RuntimeException 或 @ 987654323@ 班。 ErrorRuntimeException 的子类不是检查异常,您可以放置​​ try/catch 或者编译器不关心。由于ImageIcon 的构造函数不会抛出任何类型的异常,并且如果图像不存在将简单地返回 null,因此无需执行 try/catch 块。*

【讨论】:

  • RuntimeException 的任何子类(包括 RuntimeException 本身)也未选中。
  • @yshavit 更新了我的答案,使其更准确。
  • 那么,当ImageIcon因为文件不存在而返回null时,是不是集成了异常处理?
  • @Mikalichov 我不确定具体是如何在内部为该构造函数实现的,但很可能它捕获了异常,然后通过返回 null 来“处理”它。
  • 好的,我想我还是做得更好,谢谢!编辑:实际上,有没有办法事先知道函数是否抛出检查/未经检查的异常?还是“尝试和希望”的唯一途径?
【解决方案2】:

即使该文件现在存在,在您的系统上,它也可能在以后不存在。或者您可以将此代码提供给没有src/square.jpg 的人。或者可能会出现硬件故障,您的硬盘驱动器上的某些内容被损坏并意外删除了 src/square.jpg。也许用户甚至可能只是删除文件。

Java 中的异常处理迫使您考虑如果发生非常糟糕的事情(例如 src/square.jpg 丢失),在最坏的情况下会发生什么。你会崩溃吗?可以像什么都没发生一样继续吗?您可以在 catch 子句中决定如何处理这些故障模式。

某些函数不需要您处理异常,因为在您可以合理预期处理的函数中确实没有任何可能出错的地方。

【讨论】:

    【解决方案3】:

    1) 为什么我必须在文件确实存在时添加 ExceptionHandler?

    因为 Java 编译器无法知道该文件是否会在某个任意运行时实际存在。 (假设文件在您编译之后但在您运行程序之前被删除?)基本上,您的代码必须始终具有处理预期不会发生但无论如何可能发生的条件所需的逻辑。

    2) 为什么我必须为某些函数而不是其他函数添加 ExceptionHandler?

    可能有多种原因,但您可能会看到以下原因。每个方法都会抛出异常,但如果它们向上传播,则每个 try/catch 块或每个方法都会捕获异常。在您的示例中,您可以将每个对 ImageIcon 构造函数的调用包装在其自己的 try/catch 块中,或者将两者一起包装,具体取决于您要执行的操作:

    try {
      icon1 = new ImageIcon("f1.jpg");
    } catch (Exception e) { /* Handle the case for missing "f1.jpg". */ }
    try {
      icon2 = new ImageIcon("f2.jpg");
    } catch (Exception e) { /* Handle the case for missing "f2.jpg". */ }
    

    相比:

    try {
      icon1 = new ImageIcon("f1.jpg");
      icon2 = new ImageIcon("f2.jpg");
    } catch (Exception e) { /* Handle the case for missing "f1" or "f2". */ }
    

    【讨论】:

    • 好的,我可以向 ImageIcon 添加一个异常处理程序;这是否意味着 ImageIcon 在找不到文件时返回 null,并返回存在异常?如果是这样,为什么在这种情况下不需要事先处理可能的异常?无论如何,非常感谢这个答案,我明白了。
    • @Mikalichov:如果方法抛出异常,则控制流跳转到异常处理程序(例如 catch 块);所以在这种情况下,函数根本不会返回,所以左侧值(例如“icon1”)不会发生赋值,保留它在尝试调用构造函数之前的值。
    【解决方案4】:

    因为 Java 区分所谓的“已检查”和“未检查”异常。它是许多激烈争论的根源,关于是否应该存在未经检查的异常,以及 API 方法是否应该抛出异常。

    根据 Java Trails:

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

    这就是所谓的道理。

    阅读更多关于“争议”here

    【讨论】:

    • 好吧,至少我不是唯一一个想知道的人。但是必须为每个函数管理异常会很痛苦......但这是另一个争论,感谢您的洞察力
    【解决方案5】:

    当文件确实存在时,为什么我必须添加一个 ExceptionHandler?

    它现在存在,但不能保证它每次都会在那里。可能由于很多原因而发生。如果没有文件,做InputStream和相关的业务逻辑是没有意义的,你的业务肯定不会成功。

    为什么我必须为某些函数添加 ExceptionHandler 而不是其他函数?

    像 Imageicon 这样的一些类,它们不是您的业务处理的障碍。如果它们不存在,那没关系,您仍然可以继续使用您的核心业务逻辑。

    我觉得这是一些类强制异常而一些不是的主要原因。

    【讨论】:

      【解决方案6】:

      检查异常背后的想法是,它们使函数的调用者能够知道哪些异常可能会从中逃脱。如果您不想捕获 FileNotFoundException,则不必实际捕获,只要将 throws FileNotFoundException 添加到您的方法签名中,让您的调用者知道这样的异常可能会从您的方法中逃脱。

      这个概念很好,但不幸的是,没有一种简洁的方式来说明您想要捕获所有不太严重的异常并将它们包装到调用者的通用异常类型中。在很多情况下,当发生异常时,您想要传达的真正信息要么是“方法没有完成,但系统似乎没有着火并且任何副作用都已撤消”,或者“方法没有完成,系统似乎没有着火,但可能还有其他副作用。”如果有一种简洁的语法来指示应该捕获异常并将其包装为上述格式之一的代码区域,那将会很有帮助,但可惜没有。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-03-27
        • 1970-01-01
        • 1970-01-01
        • 2013-09-07
        • 1970-01-01
        相关资源
        最近更新 更多