Input / OutputStreams在销毁时是否关闭?

Java中的InputStreams和OutputStreams是否在销毁时关闭()? 我完全理解这可能是糟糕的forms(特别是在C和C ++世界),但我很好奇。

另外,假设我有以下代码:

private void foo() { final string file = "bar.txt"; Properties p = new Properties(); p.load( new FileInputStream(file) ); //... } 

无名的FileInputStream是否在p.load()之后超出范围,因此被销毁,有点像C ++作用域规则? 我尝试在Google上搜索java的匿名变量范围,但这并没有出现我想象的那样。

谢谢。

第一个答案:在Java中没有“破坏”(在C ++意义上)。 只有垃圾收集器,当它看到一个准备收集的对象时,可能会或可能不会唤醒并完成其工作。 Java中的GC通常是不值得信任的。

第二个答案:有时是,有时没有,但不值得冒险。 来自Elliote Rusty Harold的Java IO

例如,并非所有流都需要关闭 – 字节数组输出流不需要关闭。 但是,与文件和网络连接关联的流应始终在完成后关闭。 例如,如果您打开一个文件进行写入而忽略了在您通过时关闭它,则可能会阻止其他进程读取或写入该文件。

根据Harold的说法,输入或输出流也是如此。 有一些例外(他注意到System.in),但一般来说,如果你在完成后没有关闭文件流,你就冒了风险。 并在finally块中关闭它们,以确保即使抛出exception也会关闭它们。

我曾经假设流最终会通过垃圾收集自动关闭,但传闻证据表明无法手动关闭它们会导致资源泄漏。 你会想要做这样的事情:

 InputStream stream = null; try { stream = new FileInputStream("bar.txt"); Properties p = new Properties(); p.load(stream); } catch(Exception e) { // error handling } finally { closeQuietly(stream); } 

closeQuietly()是Apache的commons-io库中IOUtils一个方法。

不,Java中没有析构函数。 可能存在对该对象的其他引用,即使在对该对象的一个​​特定引用超出范围(或被修改)之后也是如此。 如果该对象不再可访问,则该流可能在稍后的某个时间调用其终结器,这将关闭该流。

Properties.load是特殊的,它关闭传递给它的流。 编辑: Properties.loadFromXML是我似乎在大约五年前考虑过的特殊方法。 (API doc应该先说而不是之后。)谢谢@tzimnoch。

变量超出范围,因此被销毁。 但在Java中, 变量和变量所指向对象之间存在很大的区别。

仅当变量超出范围时, 指向对象才会被销毁。 只有当Java运行时引擎决定它想要破坏任何范围内变量未指向的对象时,才会销毁该对象。

简短的回答是“也许,但不要赌它!”。

实现FileInputStream的类堆栈中的某个类是一个具有finalizer器的类,该finalizer将在运行时有效地关闭流(并释放资源)。

问题是无法保证终结器能够运行。 引自JLS(第12.6节):

Java编程语言没有指定调用终结器的时间,除非说它将在重用对象的存储之前发生。

这使得流终结成为问题:

  1. 如果您的Stream对象永远不会变成垃圾,它将永远不会被最终确定。
  2. 如果您的Stream对象是终身对象,则可能需要很长时间才能进行垃圾回收和最终确定。
  3. 在执行终结器之前,在将对象标识为无法访问之后,JVM可能需要执行额外的GC周期。 这肯定是JLS允许的!
  4. 从技术上讲,JVM永远不会执行终结器是合法的,只要它永远不会使用终结器重用对象存储。 (我不知道任何生产质量的JVM采用这条线,但你永远不知道…)