【问题标题】:Java check if file.exists() does not work with ImageIO.read(file)Java 检查 file.exists() 是否不适用于 ImageIO.read(file)
【发布时间】:2015-06-01 21:56:55
【问题描述】:

我知道如何通过 if/else 检查文件是否存在,它在我之前的作业中有效:

File f = new File("file.txt");
    if (f.exists() && f.canRead()) {
        // Code goes here.
    } else { } // Error message.

但是,如果我对缓冲图像做同样的事情,就像这样:

private BufferedImage img;

File f = new File("pic.png");
    if (f.exists() && f.canRead()) {
        img = ImageIO.read(f);
    } else { } // Error message.

然后编译器要求 try/catch 或 @throws 违背了目的。我究竟做错了什么?将我所有的精灵打包在一个 .jar 中并将它们称为资源会更好吗(我打算在完成后这样做)?

如果这很重要,我在 JRE 1.6 上运行,文件检查在 switch case 语句中。

【问题讨论】:

  • 我现在使用 try/catch 并且它有效,但我的教授坚持认为,如果我能提供帮助,我必须尽量避免异常。
  • @theUG.. 我认为,通过说try to avoid exception - 你的教授的意思是,处理它们。
  • 你需要向你的教授澄清他通过避免例外的意思。大多数 IO 方法都会抛出 IOException
  • @RohitJain 我们与他进行了一些详细的讨论,他的意思是,如果我确切知道会发生什么,我应该尝试处理它而不诉诸 try/catch fall -返回。
  • @RohitJain 你的答案去哪儿了?我认为这是迄今为止最好的,我想通过更多问题来澄清一些事情。

标签: java image file-io


【解决方案1】:

使用 try/catch 块 完全没有破坏目的。

即使文件存在并且可读,也有很多其他异常可以抛出。

可能的例子:

  • 文件在读取过程中被删除
  • 文件读取权限被撤销
  • 磁盘驱动器在读取时被拔掉/卸载

ImageIO.read() 方法抛出 IOException

没有办法避免抓住它或扔掉它。

【讨论】:

  • @UdoHeld - 我知道......我想我很难想出更多的例子。随意通过编辑添加一些。
【解决方案2】:

作为一个已检查的异常,您必须捕获它。它是否真的被抛出并不重要。您的手动错误处理将无法处理所有情况。假设您正在从网络驱动器读取数据,并且读取时连接可能会中断。

更新查看上面的 cmets。如前所述,您必须构建一个 try/catch 块,因为可以抛出异常并处理每个规范所需的已检查异常。通过事先检查,您已经避免了FileNotFoundException 的风险。有时您甚至将我归类为实现根本不会引发任何错误的地方,但由于它在接口中,您仍然必须处理它,例如ByteArryOutputStream.close().

【讨论】:

  • 所以,我认为不同之处在于强制的@throws 声明(它只是沉没在我之前阅读的关于异常传播到堆栈直到处理或程序崩溃的内容中。所以,就我而言,虽然我不会担心远程或可移动驱动器,实际的问题是,例如,阻止读取的损坏文件?
【解决方案3】:

我们与他进行了一些详细的讨论,他的意思是,如果我确切知道会发生什么,我应该尝试处理它,而不是诉诸 try/catch 回退。

如果你的导师告诉你应该进行测试以避免 IO 异常,那么我担心他完全错了。

  1. 大多数 I/O 操作都声明为抛出 IOException,这是一个检查异常。当您调用这些方法时,您的代码必须处理或传播异常。你没有选择。 Java 语言要求所有检查的异常都这样做。

  2. 测试所有可能的 IOExceptions 是不切实际的。例如,此代码可能会失败:

      if (f.exists() && f.canRead()) {
          os = new FileInputStream(f);
      }
    

    为什么?因为构造函数可能会抛出 IOException 的原因还有其他,例如硬盘错误、网络错误(如果文件在远程挂载的文件系统上)、强制访问控制失败等。除了尝试打开文件之外,通常无法通过任何其他方式检测潜在条件。

  3. 然后是竞争条件的问题。在上面的示例中,在测试文件可读性和尝试打开它之间有一个小的时间窗口。在那个时间窗口内,可能其他线程,或者一些外部进程会删除文件或改变它的访问权限,从而导致打开失败。无法关闭该时间窗口。

那么问题来了,为什么您甚至想要避免异常。我怀疑你的老师是“例外否认者”;即其中一个人将“例外只应用于例外的事情”的建议推向了不合逻辑的极端。

“异常应该只用于特殊情况”的建议基本上是说您不应该过度使用异常,因为它们有点昂贵。但贵是相对的。

在这个例子中,对File.exists()File.canRead() 的调用也很昂贵,因为它们都需要一个系统调用,而这将花费数千个时钟周期。更糟糕的是,除非你的应用程序有什么不寻常的地方,否则这两个测试将会成功……所以你刚刚做了两个不必要的系统调用来避免无论如何都不会抛出的异常。除非这些测试失败的可能性很高(即 > 50%),否则更有效跳过测试,尝试打开并在发生异常时处理异常。


我不会给我的教授贴上这样的标签。我们没有专门讨论 I/O 的复杂性,更多的是笼统地说,如果我能避免它,我应该避免它。我不知道(他也没有详细说明)有一些非常不可避免的例外情况。

好的。所以你的教授没有这么说。对他有好处。

是的,它们在不止一种意义上是不可避免的。

至于拒绝使用 .exists() 检查,对性能的影响有那么大吗?

是的。看到这个 - Syscall overhead

或者如果你不相信,写一个微基准并测量它。并将其与抛出和捕获异常的开销进行比较。

会是桌面版和移动版之类的问题吗?

不。或者对于操作系统使用虚拟内存来阻止一个“应用程序”干扰另一个“应用程序”的移动设备来说当然不是。

为了排除故障/调试(如果我可以更好地查明问题同时避免程序崩溃)的清晰度是否值得?

好吧,我认为这段代码:

try (InputStream is = new FileInputStream(f)) {
    // use file
} catch (IOException ex) {
    System.err.println("IO error: ": ex.getMessage());
}

...比这更简单,更容易维护和调试:

if (!f.exists()) {
    System.err.println("File " + f + " does not exist");
} else if (!f.isDirectory()) {
    System.err.println("File " + f + " is a directory");
} else if (!f.canRead()) {
    System.err.println("File " + f + " is not readable");
} ...
} else {
    try (InputStream is = new FileInputStream(f)) {
        // read
    } catch (IOException ex) {
        System.err.println("IO error: "+ ex.getMessage());
    }
}

你不同意吗?

【讨论】:

  • 我不会给我的教授贴上这样的标签。我们没有专门讨论 I/O 的复杂性,更多的是笼统地说,如果我能避免它,我应该避免它。我不知道(他也没有详细说明)有一些非常不可避免的例外情况。
  • 至于拒绝使用.exists()check,性能打击有那么大吗?会是桌面与移动设备之类的问题吗?为排除故障/调试(如果我可以更好地查明问题同时避免程序崩溃)的清晰度进行权衡是否值得?
【解决方案4】:

如果你看到ImageIO.read方法的声明:-

public static BufferedImage read(File input)
                          throws IOException

它声明要抛出一个IOException,它是一个CheckedException。因此,在调用该方法时,您要么需要在try-catch block 中处理它。或者,声明它在你使用它的方法中被抛出:-

File f = new File("pic.png");
if (f.exists() && f.canRead()) {
    try {
        img = ImageIO.read(f);
    } catch (IOException e) {
        e.printStackTrace();
    }
} else { } // Error message.

但是,如果你没有像上面的情况那样处理它,那么你必须在方法的throws子句中声明那个方法,以便将它传播到caller,以便它处理它.

一般情况下,任何已检查的异常要么被处理,要么传播给调用者,逐渐到达main方法,在这种情况下,如果main方法不处理,则由JVM处理,从而导致您的程序终止。

通常,最好在方法本身中处理引发异常的异常,并在需要时将适当的消息返回给调用者。

【讨论】:

  • 是的,我没有意识到read 方法要求必须处理异常。在我不得不主要处理避免 FileNotFound 和 NullPointer 异常之前,但我不熟悉一些可能导致 IOException 的场景,如此处的一些答案中所示。
  • @theUg.. 您可以随时查看文档以检查特定方法抛出的所有异常。与文档成为朋友。他们应该是你第一个去的,以防你遇到API的任何问题。
  • @theUg.. 您还可以在 Internet 上阅读有关 Checked 和 Unchecked 异常的更多信息。与 Checked Exception 一样,像 StackOverflowError 这样的 Unchecked Exception 不需要处理,也不需要声明为抛出。因为你不知道如何处理这些异常。实际上,除非您确切知道它们发生的原因,否则您不应该处理它们。
  • 我尝试阅读文档,只是在这种情况下我什至不知道要注意@throws 声明。但现在我知道了。 :)
  • @theUg.. 好吧,那没关系。现在你明白了这个问题。您可以在此处将其中一个答案标记为已接受,方法是单击它们旁边的箭头。
猜你喜欢
  • 1970-01-01
  • 2023-03-24
  • 2023-03-17
  • 2014-12-12
  • 2013-11-08
  • 2017-04-27
  • 2015-10-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多