Skip to content

FileOutputStream:把数据写入磁盘

读是输入,写是输出。

FileOutputStream 把内存中的字节写入磁盘文件,就这么简单。但「怎么写」「写到哪里」「之前的内容怎么办」,这些问题决定了你的程序行为。

两种写入模式

java
// 覆盖模式(默认):文件存在则清空内容
FileOutputStream fos = new FileOutputStream("output.dat");

// 追加模式:文件存在则追加到末尾
FileOutputStream fos = new FileOutputStream("output.dat", true);

覆盖 vs 追加的实战选择

java
// 日志场景:追加模式,每次运行都记录
try (FileOutputStream fos = new FileOutputStream("app.log", true)) {
    fos.write("New log entry\n".getBytes());
}

// 配置保存:覆盖模式,每次保存是最新的
try (FileOutputStream fos = new FileOutputStream("config.dat")) {
    fos.write(configBytes);
}

写入方式:与 FileInputStream 对称

单字节写入

java
try (FileOutputStream fos = new FileOutputStream("output.dat")) {
    fos.write(65);  // 写入字节 65(ASCII 'A')
    fos.write("Hello".getBytes());
}

不推荐:每次 write() 可能触发一次磁盘写入。

批量写入(推荐)

java
try (FileOutputStream fos = new FileOutputStream("output.dat")) {
    byte[] data = "Hello World".getBytes();
    fos.write(data);          // 写整个数组
    fos.write(data, 0, 5);   // 写前 5 个字节
}

带缓冲的批量写入(最佳)

java
try (
    BufferedOutputStream bos = new BufferedOutputStream(
        new FileOutputStream("output.dat"), 8192)
) {
    byte[] data = "Hello World".getBytes();
    bos.write(data);
    bos.flush();  // 强制写入磁盘
}
// BufferedOutputStream 在 close() 时自动 flush

写文本文件:编码问题

这是最常见的坑。

java
// ❌ 错误:直接写字节,中文编码依赖系统
try (FileOutputStream fos = new FileOutputStream("output.txt")) {
    fos.write("你好".getBytes());  // 系统编码不确定,Linux 是 UTF-8,Windows 可能是 GBK
}

// ✅ 正确:用 OutputStreamWriter 指定 UTF-8
try (OutputStreamWriter writer = new OutputStreamWriter(
        new FileOutputStream("output.txt"), StandardCharsets.UTF_8)) {
    writer.write("你好");
}

记住:写文本文件永远用 OutputStreamWriter,不要直接用 FileOutputStream

写二进制数据:配合 DataOutputStream

如果需要写入 int、double 等基本类型:

java
try (
    DataOutputStream dos = new DataOutputStream(
        new BufferedOutputStream(
            new FileOutputStream("data.bin")))
) {
    dos.writeInt(42);
    dos.writeDouble(3.14159);
    dos.writeBoolean(true);
}

关于 DataOutputStream 的更多内容,参见数据流

flush() vs close()

java
// flush():只刷新缓冲区,不关闭流
// close():先 flush,再关闭流

// BufferedOutputStream 需要 flush
try (BufferedOutputStream bos = new BufferedOutputStream(
        new FileOutputStream("out.dat"))) {
    bos.write("data".getBytes());
    bos.flush();  // 立即写入磁盘,但不关闭
    // 还可以继续写
}

// 普通 FileOutputStream 没有缓冲,flush() 没什么用
try (FileOutputStream fos = new FileOutputStream("out.dat")) {
    fos.write("data".getBytes());
    fos.flush();  // 等同于空操作
}

避坑指南

目录不存在

java
// ❌ 目录不存在会抛异常
new FileOutputStream("/nonexistent/path/file.txt");

// ✅ 先创建父目录
File file = new File("/nonexistent/path/file.txt");
file.getParentFile().mkdirs();  // 创建所有不存在的父目录
try (FileOutputStream fos = new FileOutputStream(file)) {
    fos.write("data".getBytes());
}

忘记 close()

java
// ❌ 数据还在缓冲区,程序退出了,文件是空的
FileOutputStream fos = new FileOutputStream("out.dat");
fos.write("data".getBytes();
// 没有 close(),数据没写入磁盘

// ✅ 用 try-with-resources
try (FileOutputStream fos = new FileOutputStream("out.dat")) {
    fos.write("data".getBytes());
}
// 自动 close,数据完整写入

方法对照表

方法说明
write(int b)写单字节(低 8 位)
write(byte[] b)写整个数组
write(byte[] b, int off, int len)写数组区间
flush()强制刷新缓冲区到磁盘
close()关闭流(会自动 flush)

写入口诀:覆盖还是追加,编码必须指定,关闭交给 try-with-resources。

基于 VitePress 构建