当前位置: 首页 > 面试题库 >

Java在获取/关闭资源时尝试/捕获/最终的最佳实践

郦兴德
2023-03-14
问题内容

在进行学校项目时,我编写了以下代码:

FileOutputStream fos;
ObjectOutputStream oos;
try {
    fos = new FileOutputStream(file);
    oos = new ObjectOutputStream(fos);

    oos.writeObject(shapes);
} catch (FileNotFoundException ex) {
    // complain to user
} catch (IOException ex) {
    // notify user
} finally {
    if (oos != null) oos.close();
    if (fos != null) fos.close();
}

问题在于Netbeans告诉我这些resource.close()行抛出一个IOException,因此必须被捕获或声明。它还抱怨oos并且fos可能尚未初始化(尽管检查为空)。

似乎整个问题都在此停了下来,这似乎有些奇怪IOException

我的下意识解决方法是这样做:

} finally {
    try {
        if (oos != null) oos.close();
        if (fos != null) fos.close();
    } catch (IOException ex) { }
}

但是,这深深地困扰着我,让我感到肮脏。

我来自C#背景,我只是在这里利用using块,因此我不确定处理此问题的“正确”方法是什么。

解决这个问题的正确方法 什么?


问题答案:

如果您试图从源头捕获并报告所有异常,那么更好的解决方案是:

ObjectOutputStream oos = null;
try {
   oos = new ObjectOutputStream(new FileOutputStream(file));
   oos.writeObject(shapes);
   oos.flush();
} catch (FileNotFoundException ex) {
    // complain to user
} catch (IOException ex) {
    // notify user
} finally {
    if (oos != null) {
        try {
            oos.close();
        } catch (IOException ex) {
            // ignore ... any significant errors should already have been
            // reported via an IOException from the final flush.
        }
    }
}

笔记:

  • 标准的Java包装器流,读取器和写入器都传播close并传播flush到它们的包装器流等。因此,您只需要关闭或刷新最外面的包装器。
  • 在try块的末尾显式刷新的目的是使(实际)处理程序IOException能够看到任何写入失败1。
  • 当您对输出流进行关闭或刷新时,由于光盘错误或文件系统已满,可能会出现“一次蓝月亮”的异常。 您不应该压这个异常!

如果您经常需要“关闭忽略IOExceptions的可能为空的流”,则可以为自己编写一个帮助方法,如下所示:

public void closeQuietly(Closeable closeable) {
    if (closeable != null) {
        try {
            closeable.close();
        } catch (IOException ex) {
            // ignore
        }
    }
}

那么您可以将之前的finally块替换为:

} finally {
    closeQuietly(oos);
}

(另一个答案指出,closeQuietlyApache Commons库中已经有一种方法…如果您不介意为项目的10行方法添加依赖项
。UPDATE :请注意,这些方法在2.6版的API。)

但请注意,仅closeQuietly真正 与IO异常无关的流上使用。

1-使用try-with-resources时没有必要。

论问题flush()close()人们的询问:

  • 标准的“过滤器”和“缓冲的”输出流和编写器具有API约定,该约定规定close()将刷新所有缓冲的输出。您 应该 发现执行输出缓冲的所有其他(标准)输出类的行为也将相同。因此,对于标准类,flush()在之前立即调用是多余的close()
  • 对于自定义类和第三方类,您需要进行调查(例如,阅读javadoc,查看代码),但是任何close()不刷新缓冲数据的方法都可能 被破坏
  • 最后,存在flush()实际起作用的问题。javadoc说的是这个(用于OutputStream…)

如果此流的预期目标是底层操作系统(例如文件)提供的抽象,则刷新该流可确保仅将先前写入该流的字节传递给操作系统以进行写入;它不能保证它们实际上已写入到物理设备(例如磁盘驱动器)中。

所以…如果您希望/想象调用可以flush()保证您的数据将持久存在,那 您就错了!
(如果您需要执行此类操作,请查看FileChannel.force方法…)

另一方面,如果可以使用Java 7或更高版本,则@Mike Clark的答案中所述的“新” try-with-resources是最佳解决方案。

如果您的新代码未使用Java 7或更高版本,则可能陷入了更深层次的挖掘。



 类似资料:
  • 问题内容: 我经常遇到如下情况: 仍然需要尝试-最终在内部捕获块。 克服此问题的最佳实践是什么? 问题答案: 写一个类,其中包含捕获和记录此类异常的方法,然后根据需要使用。 您最终会看到如下内容: 您的客户端代码将类似于: 更新: 自Java 7开始,各种JDBC接口都得到了扩展,而以上代码回答了原始问题,如果您直接针对JDBC API编写代码,则现在可以对其进行结构化:

  • 问题内容: 我有一个需要类似以下内容的场景 在我的尝试,我会,数据,将其与基于处理其他数据集。 在随后。有可能阻止吗?下面是伪代码: 问题答案: 无需创建表,您只需声明一个表变量(查询结束时该变量将自动消失)。

  • 鉴于: 我想尝试,并重试,连接到服务器3次才放弃。 我可以把整个try/catch放在一个循环中,但是这是否符合Java的“最佳实践”。从我对该主题的回忆来看,这将是对语句的误用。再说一次,我可能完全错了。你怎么认为?

  • 问题内容: 我一直在看代码,并且看到了尝试资源的机会。我以前使用过标准的try-catch语句,看起来它们在做同样的事情。所以我的问题是“ 尝试使用资源”与“尝试捕获 ”之间的区别是什么,哪个更好。 这是尝试使用资源: 问题答案: 尝试使用资源的重点是确保可靠地关闭资源。 当你不使用try-with-resources时,存在一个潜在的陷阱,称为异常屏蔽。当try块中的代码引发异常,而finall

  • 问题内容: 我想在finally块中关闭流,但是它抛出一个,因此看来我必须在块中嵌套另一个块才能关闭流。这是正确的方法吗?似乎有点笨拙。 这是代码: 问题答案: 似乎有点笨拙。 它是。至少java7尝试使用资源可以解决该问题。 在java7之前,您可以创建一个吞咽它的函数: 或将try …最终放入try catch中: 它比较冗长,并且finally中的异常会在try中隐藏一个,但从语义上讲它更接

  • 在 中,尝试捕获最终阻止的工作原理是什么? 所以如果有例外,我知道它会跳到捕获块,然后跳到最后的块。 但是如果没有错误,catch块不会运行,但是finally块会运行吗?