I/O的基础知识
流
Java 程序通过流执行 I/O. 流是一种抽象, 要么产生信息, 要么使用信息. 流通过 Java 的 I/O 系统连接到物理设备.
所有流的行为方式都是相同的, 尽管与他们链接的物理设备是不同的.
因此, 可以为任意类型的设备应用相同的 I/O 类和方法. 这意味着可以将许多不同类型的输入-磁盘文件,键盘或网络 socket, 抽象为输入流. 与之对应, 输入流可以引用控制台,磁盘文件或网络连接. 流是处理输入/输出的一种清晰方式.
字节流和字符流
Java 定义了两种类型的流: 字节流额字符流.
字节流为处理字节的输入和输出提供了方法. 例如, 当读取和写入二进制数据时, 使用的就是字节流.
字符流为处理字符的输入和输出提供了方便的方法. 它们使用 Unicode 编码, 所以可以被国际化. 此外, 在某些情况下字符流比字节流高效.
另外一点: 在最底层, 所有 I/O 仍然是面向字节的. 基于字符的流只是为处理字符提供了一种方便和高效的方法.
字节流
字节流是通过两个类层次定义的. 在顶级是两个抽象类: InputStream
和 OutputStream
.
每个抽象类都有几个吃力何种不同设备的具体子类, 例如磁盘文件, 网络连接甚至内存缓冲区.
下面列出了 java.io
包中的字符流类
流 类 | 含 义 |
---|---|
BufferedInputStream | 缓冲的输入流 |
BufferedOutputStream | 缓冲的输出流 |
ByteArrayInputStream | 读取字节数组内容的输入流 |
ByteArrayOutputStream | 向字节数组写入内容的输出流 |
DataInputStream | 包含读取 Java 标准数据类型的方法的输入流 |
DataOutputStream | 包含写入 Java 标准数据类型的方法的输出流 |
FileInputStream | 读取文件内容的输入流 |
FileOnputStream | 向文件写入内容的输出流 |
FilterInputStream | 实现InputStream |
FilterOutputStream | 实现OutputStream |
ObjectInputStream | 用于对象的输入流 |
ObjectOutputStream | 用于对象的输出流 |
PipedInputStream | 输入管道 |
PipedOutputStream | 输出管道 |
PrintStream | 包含print() 和 println() |
抽象类 InputStream
和 OutputStream
定义了其他流类实现的一些关键方法. 其中最重要的两个方式 read()
和 write()
, 这两个方法分别读取和写入字节数据. 每个方法都有抽象形式, 派生的流类必须重写这两个方法.
字符流
字符流是通过两个类层次定义的. 在顶层是两个抽象类: Reader
和 Writer
. 这两个抽象类处理 Unicode 字符流.
下面列出 java.io
包下的字符流类
流 类 | 含 义 |
---|---|
BufferedReader | 缓冲的输入字符流 |
BufferedWriter | 缓冲的输出字符流 |
CharArrayReader | 从字符数组读取内容的输入流 |
CharArrayWriter | 从字符数组写入内容的输出流 |
FileReader | 从文件读取内容的输入流 |
FileWriter | 向文件写入内容的输出流 |
FilterReader | 过滤的读取器 |
FilterWriter | 过滤的写入器 |
InputStreamReader | 将字节转换成字符的输入流 |
OutputStreamWriter | 将字符转成成字节的输出流 |
PipedReader | 输入管道 |
PipedWriter | 输出管道 |
PrintWriter | 包含 print() 和 println() |
StringReader | 从字符串读取内容的输入流 |
StringWriter | 向字符串写入内容的输出流 |
抽象类 Reader
和 Writer
定义了其它几个流类实现的重要方法. 最重要的两个方法是 read()
和 write()
, 这两个方法分别读取和写入字符数据. 每个方法都有抽象形式, 派生的流类必须实现这两个方法.
PrintWriter 类
尽管使用 System.out
向控制台输出可以接受的, 但是最好将其用于调试. 对于实际的程序, 使用 java 向控制台输出的推荐方法是通过 PrintWriter
流.
PrintWriter 类定义了几个构造函数, 在此将使用的构造函数如下所示:
PrintWriter(OutputStream outputStream, boolean flushingOn)
其中, outputStream 是OutputStream 类型的对象, flushingOn 控制Java 是否在每次调用 println()
方法时刷新输出流. 如果flushingOn 为true, 就自动刷新; 如果为false, 那么不会自动刷新.
PrintWriter 支持 print()
和 println()
方法. 因此, 可以使用与System.out 相同的方式使用它们. 如果参数不是简单类型, PrintWriter 方法会调用对象的 toString()
方法, 然后输出结果.
为了使用 PrintWriter 向控制台输出, 为输出流制定 System.out, 然后在每个新行之后刷新流.
public class PrintWriterDemo {
public static void main(String args[]) {
PrintWriter pw = new PrintWriter(System.out, true);
pw.println("This is a string");
int i = -7;
pw.println(i);
}
}
读/写文件
对于读/写文件, 两个最常用的流是 FileInputStream
和 FileOutputStream
, 这两个类创建与文件链接的字节流.
为了打开文件, 只需要创建简单的创建这些类中某个类的对象, 指定文件名作为构造函数的参数即可.
FileInputStream(String fileName)
FileOutputStream(String fileName)
文件使用完之后必须关闭. 关闭文件是通过 close()
方法完成的, FileInputStream
和 FileOutputStream
都实现了该方法.
void close()
关闭文件会释放文件分配的系统资源, 从而允许其他的文件使用这些资源. 关闭文件失败会导致"内存泄漏", 因为未使用的资源没有被释放.
注意: 从 JDK 7 开始,
close()
方法是由java.lang
包中的AutoCloseable
接口指定的.java.io
包中的 Closeable 接口竭诚了AutoCloseable接口. 所有流类都实现了这两个接口, 包括FileInputStream
和FileOutputStream
.
可以使用两种方法关闭文件, 第一种是传统方法, 当不在需要文件时显式调用close()方法. 第二种方式是使用带资源的try语句, 这种try语句是由JDK 7 新增的, 当不再需要文件时能够自动关闭文件.
try (FileInputStream fin = new FileInputStream(args[0]);
FileOutputStream fout = new FileOutputStream(args[1])) {
// ...
只有对于那些实现了
AotoCloseable
接口的资源, 才能使用带资源的 try 语句.
下面是关于带资源的 try 语句的3个关键点:
- 带资源的 try 语句管理的资源必须是实现了
AutoCloseable
接口的类的对象. - 在 try 代码中声明的资源被隐式声明为
final
- 通过使用分号分隔每个声明可以管理多个资源.
此外请记住, 所声明资源的作用域被限制在带资源的 try 语句中.
带资源的 try 语句的主要优点是: 当 try 代码块结束时, 资源会被自动关闭. 因此, 不可能会忘记关闭流. 使用带资源的 try 语句, 通常可以时源代码更短, 更清晰, 更容易维护.