💡💡💡 结尾带Stream的是字节流,结尾带Write、Reader 的是字符流。
_I/O 流_代表输入源或输出目的地。流可以表示许多不同类型的源和目的地,包括磁盘文件、设备、其他程序和内存阵列。
流支持许多不同类型的数据,包括简单字节、原始数据类型、本地化字符和对象。有些流只是传递数据;有些流只是传递数据。其他人以有用的方式操纵和转换数据。
无论它们内部如何工作,所有流都向使用它们的程序呈现相同的简单模型:流是数据序列。
文件字节流
字节文件流就是通过字节将文件读取到内存中来。程序使用_字节流_来执行8位字节的输入和输出。所有字节流类都是从 InputStream
和 派生的OutputStream
。
我们将重点关注文件 I/O 字节流 FileInputStream
和 FileOutputStream
. 其他类型的字节流的使用方式大致相同;它们的区别主要在于构造方式。
FileInputStream 从文件系统中的文件中获取输入字节。可用的文件取决于主机环境。
FileInputStream 用于读取原始字节流,例如图像数据。要读取字符流,请考虑使用 FileReader.
FileInputStream in = null;
FileOutputStream out = null;
流的关闭
当不再需要流时关闭它非常重要 – 如此重要以至于CopyBytes
使用finally
块来保证即使发生错误也将关闭两个流。这种做法有助于避免严重的资源泄漏。
一种可能的错误是CopyBytes
无法打开一个或两个文件。当发生这种情况时,与该文件对应的流变量永远不会改变其初始null
值。这就是为什么CopyBytes
在调用之前确保每个流变量包含一个对象引用close
。
何时不使用字节流
CopyBytes
看起来像是一个普通的程序,但它实际上代表了一种应该避免的低级 I/O。由于xanadu.txt
包含字符数据,因此最好的方法是使用字符流,字节流只能用于最原始的 I/O。
那么为什么要谈论字节流呢?因为所有其他流类型都是建立在字节流之上的。
文件字符流
Java 平台使用 Unicode 约定存储字符值。字符流 I/O 自动将此内部格式与本地字符集进行转换。在西方语言环境中,本地字符集通常是 ASCII 的 8 位超集。
对于大多数应用程序,字符流 I/O 并不比字节流 I/O 复杂。使用流类完成的输入和输出会自动转换为本地字符集或从本地字符集转换。使用字符流代替字节流的程序会自动适应本地字符集并为国际化做好准备——所有这些都不需要程序员付出额外的努力。
如果国际化不是优先考虑的事项,您可以简单地使用字符流类,而不必过多关注字符集问题。稍后,如果国际化成为优先事项,您的程序可以进行调整,而无需进行大量重新编码。
FileReader fileReader = null;
FileWriter fileWriter = null;
面向行的 I/O
字符 I/O 通常出现在比单个字符更大的单元中。一个常见的单位是行:末尾带有行终止符的字符串。行终止符可以是回车/换行序列 ( "\r\n"
)、单个回车 ( "\r"
) 或单个换行 ( "\n"
)。支持所有可能的行终止符允许程序读取在任何广泛使用的操作系统上创建的文本文件。
让我们修改CopyCharacters
示例以使用面向行的 I/O。为此,我们必须使用两个以前从未见过的类, BufferedReader
和 PrintWriter
。我们将在缓冲 I/O和格式化中更深入地探讨这些类。现在,我们只对它们对面向行的 I/O 的支持感兴趣。
该 CopyLines
示例调用BufferedReader.readLine
和PrintWriter.println
一次进行一行输入和输出。
fileReader.readLine()
调用readLine
返回一行文本。CopyLines
使用 输出每一行println
,它会附加当前操作系统的行终止符。这可能与输入文件中使用的行终止符不同。
除了字符和行之外,还有许多方法可以构建文本输入和输出。