在阅读有关Java的初学者书籍时,我读到一旦不再需要流,必须始终关闭它。为什么会这样呢?保持打开状态有什么问题?
请考虑以下示例:
import java.io.*;
public class SOStreamTest {
public static void main(String[] args){
try{
FileWriter writer = new FileWriter("Foo.txt");
writer.write("hello foo!");
//Writer.close(); <-!Line in Question -->
}catch(IOException ex){
ex.printStackTrace();
}
}
}
正如预期的那样,会出现一条警告消息,"资源泄漏:"编写器"永远不会关闭。
首先,你期望你的应用程序将在main的底部结束,但是main是一个普通的方法,可能会被继续运行的其他代码调用。
您不希望流保留的原因是它们往往与操作系统资源相关联,这比内存更受约束。
套接字更加麻烦,因为您可能会同时保留程序和远程主机的资源。
您知道在Windows PC上,并且您尝试删除文件,但它正被另一个程序使用?这就是为什么您应该始终关闭文件输入或输出流的原因之一。
另一个例子,这次对于 Linux,是每个文件输入或输出流都需要称为文件句柄的东西。给定的 linux 系统只允许存在一定数量的文件句柄,如果达到最大值,可能会发生不好的事情。
关闭流时,将释放文件句柄。
其他输入和输出流可能会导致类似的问题,例如,未能关闭网络流可能会打开与另一台计算机的连接。这样做几千次,您将耗尽端口或杀死您正在连接的机器。
关闭流的重要性可以减少到两个一个要点:
-
首先,流通常与某些系统资源(套接字、文件、数据库连接(相关联,一般来说,将其作为昂贵的资产处理是一种很好的做法,因为它可能被其他进程共享,甚至会减慢系统速度。
-
此外,如果您不关闭流,它在内存中的表示将保持活动状态,因为不会有提示告诉垃圾收集器可以删除此对象。
因此,通过关闭流,您可以明智地使用系统资源和进程的内存空间。
编辑:正如下面的评论部分所指出的,我所说的垃圾收集是错误的。但是,可能会发生的情况是,流可能会在被 GC 销毁之前关闭,因为许多基本流(至少在 Java 中(都包含对其finalize()
方法的close()
调用。请参阅有关此主题的SO线程。
最常见的原因之一是确保所有内容都将写入输出中。
授予应用程序在文件中写入数据的访问权限可能需要一些时间。因此,为了避免每次都请求它写入单字节,我们使用像BufferedWriter
这样的缓冲区,在其中存储大部分数据,当它已满时,它会自动请求写入访问权限,写入其存储的数据,并清理缓冲区。
但是在缓冲区未满且我们没有更多数据要写入的情况下,我们需要显式通知缓冲区将数据写入文件。我们可以通过调用它的 flush
方法,或者调用隐式调用close
flush
的方法来实现。没有它,我们的文件将不包含当前存储在缓冲区中的部分。