Java的OutputStream
是所有字节输出流的抽象基类,位于java.io
包中,提供了将字节数据写入目标(如文件、网络连接、内存等)的基本接口。本文将详细解析OutputStream
的核心概念、常用方法、设计模式及源码实现。
一、核心概念
1. 作用
OutputStream
是Java IO体系中字节输出流的顶层抽象,定义了写入字节数据的标准方法,所有具体的字节输出流类(如FileOutputStream
、ByteArrayOutputStream
、SocketOutputStream
等)都继承自它。
2. 数据目标类型
OutputStream
的数据目标可以是:
- 文件(
FileOutputStream
) - 内存(
ByteArrayOutputStream
) - 网络连接(
Socket.getOutputStream()
) - 管道(
PipedOutputStream
) - 其他流(如
BufferedOutputStream
包装其他流)
二、常用方法
1. 核心写入方法
// 写入单个字节(参数为int,但只写入低8位)
public abstract void write(int b) throws IOException;
// 写入整个字节数组
public void write(byte b[]) throws IOException;
// 写入字节数组b的从off位置开始的len个字节
public void write(byte b[], int off, int len) throws IOException;
2. 其他重要方法
// 刷新缓冲区,确保数据全部写出
public void flush() throws IOException;
// 关闭流,释放资源(关闭前会自动flush)
public void close() throws IOException;
三、源码解析
1. 抽象方法 write(int b)
public abstract void write(int b) throws IOException;
- 作用:写入单个字节数据,参数
b
是int
类型,但实际只写入其低8位(0-255
)。 - 实现要求:所有子类必须实现此方法,它是
OutputStream
最核心的方法。
2. 批量写入方法 write(byte[] b)
public void write(byte b[]) throws IOException {
write(b, 0, b.length);
}
- 作用:将整个字节数组写入输出流,调用了
write(b, 0, b.length)
的重载方法。
3. 核心重载方法 write(byte[] b, int off, int len)
public void write(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
for (int i = 0 ; i < len ; i++) {
write(b[off + i]); // 循环调用抽象方法write(int b)
}
}
- 实现逻辑:
- 检查参数合法性。
- 循环调用
write(int b)
方法,逐个写入字节数组中的元素。
- 性能问题:直接使用此方法效率较低,因为每次只写入一个字节,建议使用
BufferedOutputStream
包装以提高性能。
4. flush()
方法
public void flush() throws IOException {
// 默认实现为空,因为抽象类无需刷新缓冲区
}
- 作用:确保缓冲区中的数据全部写出到目标。
- 子类重写:如
BufferedOutputStream
会在此方法中清空内部缓冲区。
5. close()
方法
public void close() throws IOException {
// 默认实现为空,因为抽象类无需释放资源
}
- 子类重写:如
FileOutputStream
会关闭文件句柄,SocketOutputStream
会关闭网络连接。通常会先调用flush()
确保数据写出。
四、设计模式
1. 模板方法模式
OutputStream
定义了核心方法的骨架(如write(byte[])
调用write(int b)
),具体实现由子类完成,符合模板方法模式。
2. 装饰器模式
FilterOutputStream
及其子类(如BufferedOutputStream
、DataOutputStream
)通过包装其他OutputStream
实现功能增强,符合装饰器模式。
五、常见子类
1. FileOutputStream
将数据写入文件,直接关联底层文件描述符。
2. ByteArrayOutputStream
将数据写入内存字节数组,无需进行IO操作,适用于临时数据存储。
3. BufferedOutputStream
通过缓冲区提高写入性能,减少实际IO次数。
4. DataOutputStream
用于写入基本数据类型(如int
、double
),需配合DataInputStream
使用。
5. ObjectOutputStream
用于序列化对象,实现Java对象的持久化。
六、使用示例
1. 写入文件
try (OutputStream os = new FileOutputStream("test.txt")) {
String data = "Hello, World!";
os.write(data.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
2. 使用缓冲流提高性能
try (OutputStream os = new FileOutputStream("test.txt");
BufferedOutputStream bos = new BufferedOutputStream(os)) {
String data = "Hello, World!";
bos.write(data.getBytes());
bos.flush(); // 确保数据全部写出
} catch (IOException e) {
e.printStackTrace();
}
3. 写入基本数据类型
try (DataOutputStream dos = new DataOutputStream(
new FileOutputStream("data.dat"))) {
dos.writeInt(123);
dos.writeDouble(3.14);
dos.writeUTF("Hello");
} catch (IOException e) {
e.printStackTrace();
}
七、注意事项
- 资源管理:
OutputStream
使用完毕后必须调用close()
关闭,建议使用try-with-resources
语句自动关闭。 - 缓冲区刷新:使用带缓冲的流(如
BufferedOutputStream
)时,需调用flush()
确保数据写出,close()
方法会自动调用flush()
。 - 追加模式:
FileOutputStream
构造函数可传入append
参数(如new FileOutputStream("test.txt", true)
)以启用追加模式。 - 阻塞特性:传统IO是阻塞的,若需非阻塞IO,可使用
java.nio
包中的FileChannel
或SocketChannel
。
总结
OutputStream
作为Java IO体系的基础组件,定义了字节输出流的标准接口,通过抽象方法和模板模式为子类提供了统一的操作框架。理解其设计思想和源码实现,有助于高效使用各种具体流类,并在需要时自定义扩展。