从 01 开始 从 01 开始
首页
  • 📚 计算机基础

    • 计算机简史
    • 数字电路
    • 计算机组成原理
    • 操作系统
    • Linux
    • 计算机网络
    • 数据库
    • 编程工具
    • 装机
  • 🎨 前端

    • Node
  • JavaSE
  • Java 高级
  • JavaEE

    • 构建、依赖管理
    • Ant
    • Maven
    • 日志框架
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • 环境管理和配置管理-科普篇
    • Servlet
  • Spring

    • Spring基础
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC

    • SpringMVC 基础
  • SpringBoot

    • SpringBoot 基础
  • Windows 使用技巧
  • 手机相关技巧
  • 最全面的输入法教程
  • 最全面的浏览器教程
  • Office
  • 图片类工具
  • 效率类工具
  • 最全面的 RSS 教程
  • 码字工具
  • 各大平台
  • 校招
  • 五险一金
  • 职场规划
  • 关于离职
  • 杂谈
  • 自媒体
  • 📖 读书

    • 读书工具
    • 走进科学
  • 🌍 英语

    • 从零开始学英语
    • 英语兔的相关视频
    • Larry 想做技术大佬的相关视频
  • 🏛️ 政治

    • 新闻合订本
    • 反腐
    • GFW
    • 404 内容
    • 审查与自我审查
    • 互联网
    • 战争
    • 读书笔记
  • 💰 经济

    • 关于税
    • 理财
  • 💪 健身

    • 睡眠
    • 皮肤
    • 口腔健康
    • 学会呼吸
    • 健身日志
  • 🏠 其他

    • 驾驶技能
    • 租房与买房
    • 厨艺
  • 电影

    • 电影推荐
  • 电视剧
  • 漫画

    • 漫画软件
    • 漫画推荐
  • 游戏

    • Steam
    • 三国杀
    • 求生之路
  • 小说
  • 关于本站
  • 关于博主
  • 打赏
  • 网站动态
  • 友人帐
  • 从零开始搭建博客
  • 搭建邮件服务器
  • 本站分享
  • 🌈 生活

    • 2022
    • 2023
    • 2024
    • 2025
  • 📇 文章索引

    • 文章分类
    • 文章归档

晓林

程序猿,自由职业者,博主,英语爱好者,健身达人
首页
  • 📚 计算机基础

    • 计算机简史
    • 数字电路
    • 计算机组成原理
    • 操作系统
    • Linux
    • 计算机网络
    • 数据库
    • 编程工具
    • 装机
  • 🎨 前端

    • Node
  • JavaSE
  • Java 高级
  • JavaEE

    • 构建、依赖管理
    • Ant
    • Maven
    • 日志框架
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • 环境管理和配置管理-科普篇
    • Servlet
  • Spring

    • Spring基础
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC

    • SpringMVC 基础
  • SpringBoot

    • SpringBoot 基础
  • Windows 使用技巧
  • 手机相关技巧
  • 最全面的输入法教程
  • 最全面的浏览器教程
  • Office
  • 图片类工具
  • 效率类工具
  • 最全面的 RSS 教程
  • 码字工具
  • 各大平台
  • 校招
  • 五险一金
  • 职场规划
  • 关于离职
  • 杂谈
  • 自媒体
  • 📖 读书

    • 读书工具
    • 走进科学
  • 🌍 英语

    • 从零开始学英语
    • 英语兔的相关视频
    • Larry 想做技术大佬的相关视频
  • 🏛️ 政治

    • 新闻合订本
    • 反腐
    • GFW
    • 404 内容
    • 审查与自我审查
    • 互联网
    • 战争
    • 读书笔记
  • 💰 经济

    • 关于税
    • 理财
  • 💪 健身

    • 睡眠
    • 皮肤
    • 口腔健康
    • 学会呼吸
    • 健身日志
  • 🏠 其他

    • 驾驶技能
    • 租房与买房
    • 厨艺
  • 电影

    • 电影推荐
  • 电视剧
  • 漫画

    • 漫画软件
    • 漫画推荐
  • 游戏

    • Steam
    • 三国杀
    • 求生之路
  • 小说
  • 关于本站
  • 关于博主
  • 打赏
  • 网站动态
  • 友人帐
  • 从零开始搭建博客
  • 搭建邮件服务器
  • 本站分享
  • 🌈 生活

    • 2022
    • 2023
    • 2024
    • 2025
  • 📇 文章索引

    • 文章分类
    • 文章归档
  • JavaSE

    • 我的 Java 学习路线
    • 安装 Java
    • Java 数据类型

    • Java 多版本配置
    • 面向对象

    • Java 核心类

    • IO

      • IO 介绍
      • File 对象介绍
      • InputStream
      • OutputStream
        • write 方法
        • close()和 flush()
        • FileOutputStream
        • 如果文件不存在...
        • 阻塞
        • ByteArrayOutputStream
        • 小结
      • Decorator 模式
      • 读写 zip 文件
      • Properties 类
      • 读取 classpath 中的配置文件
      • 序列化与反序列化
      • Reader
      • Writer
      • PrintStream 和 PrintWriter
      • Scanner
      • 使用 Files
      • Console
      • IO 系列小结
    • Java 与时间

    • 异常处理

    • 哈希和加密算法

    • Java8 新特性

    • 网络编程

  • JavaSenior

  • JavaEE

  • JavaWeb

  • Spring

  • 主流框架

  • SpringMVC

  • SpringBoot

  • Java
  • JavaSE
  • IO
2023-02-02
目录

OutputStream

# OutputStream

OutputStream 是 Java 标准库提供的最基本的输出流,它也是一个抽象类,是所有输出流的超类。

类的定义如下

public abstract class OutputStream implements Closeable, Flushable 
1

# write 方法

OutputStream 的一个最重要的方法就是 void write(int b),签名如下:

public abstract void write(int b) throws IOException;
1

这个方法会写入一个字节到输出流。要注意的是,虽然传入的是 int 参数,但只会写入一个字节,即只写入 int 最低 8 位表示字节的部分(相当于 b & 0xff)。

# close()和 flush()

和 InputStream 类似,OutputStream 也提供了 close() 方法关闭输出流,以便释放系统资源。要特别注意:OutputStream 还提供了一个 flush() 方法,它的目的是将缓冲区的内容真正输出到目的地。

为什么要有 flush()?因为向磁盘、网络写入数据的时候,出于效率的考虑,操作系统并不是输出一个字节就立刻写入到文件或者发送到网络,而是把输出的字节先放到内存的一个缓冲区里(本质上就是一个 byte[] 数组),等到缓冲区写满了,再一次性写入文件或者网络。对于很多 IO 设备来说,一次写一个字节和一次写 1000 个字节,花费的时间几乎是完全一样的,所以 OutputStream 有个 flush() 方法,能强制把缓冲区内容输出。

通常情况下,我们不需要调用这个 flush() 方法,因为缓冲区写满了 OutputStream 会自动调用它,并且,在调用 close() 方法关闭 OutputStream 之前,也会自动调用 flush() 方法。

但是,在某些情况下,我们必须手动调用 flush() 方法。举个栗子:

小明正在开发一款在线聊天软件,当用户输入一句话后,就通过 OutputStream 的 write() 方法写入网络流。小明测试的时候发现,发送方输入后,接收方根本收不到任何信息,怎么肥四?

原因就在于写入网络流是先写入内存缓冲区,等缓冲区满了才会一次性发送到网络。如果缓冲区大小是 4K,则发送方要敲几千个字符后,操作系统才会把缓冲区的内容发送出去,这个时候,接收方会一次性收到大量消息。

解决办法就是每输入一句话后,立刻调用 flush(),不管当前缓冲区是否已满,强迫操作系统把缓冲区的内容立刻发送出去。

实际上,InputStream 也有缓冲区。例如,从 FileInputStream 读取一个字节时,操作系统往往会一次性读取若干字节到缓冲区,并维护一个指针指向未读的缓冲区。然后,每次我们调用 int read() 读取下一个字节时,可以直接返回缓冲区的下一个字节,避免每次读一个字节都导致 IO 操作。当缓冲区全部读完后继续调用 read(),则会触发操作系统的下一次读取并再次填满缓冲区。

# FileOutputStream

我们以 FileOutputStream 为例,演示如何将若干个字节写入文件流:

    OutputStream output = new FileOutputStream("readme.txt");
    output.write(72); // H
    output.write(101);  // e
    output.write(108);  // l
    output.write(108);  // l
    output.write(111);  // o
    output.close();
1
2
3
4
5
6
7

运行结果:

$ javac IODemo6FileOutputStream1.java
$ java IODemo6FileOutputStream1   
$ cat readme.txt
Hello
1
2
3
4

每次写入一个字节非常麻烦,更常见的方法是一次性写入若干字节。这时,可以用 OutputStream 提供的重载方法 void write(byte[]) 来实现:

    OutputStream output = new FileOutputStream("readme.txt");
    output.write("HelloWorld".getBytes("UTF-8"));
    output.close();
1
2
3

运行结果:

$ javac IODemo6FileOutputStream2.java  
$ java IODemo6FileOutputStream2  
$ cat readme.txt             
HelloWorld
1
2
3
4

上述代码没有考虑到在发生异常的情况下如何正确地关闭资源。写入过程也会经常发生 IO 错误,例如,磁盘已满,无权限写入等等。我们需要用 try(resource)来保证 OutputStream在无论是否发生 IO 错误的时候都能够正确地关闭。

# 如果文件不存在...

使用 new Fileoutputstream(filepath)时, 如果 filepath 指定的文件不存在, 文件输出流会帮我们自动创建一个文件。但仅仅是文件而已,如果 filepath 中包含尚未创建的目录, 就会抛出文件找不到异常。 所以在使用输出流之前, 最好先确定文件是否存在,不存在则使用 File 的 mkdirs 方法, 先创建文件目录

# 阻塞

和 InputStream 一样,OutputStream 的 write() 方法也是阻塞的。

# ByteArrayOutputStream

ByteArrayOutputStream 可以在内存中模拟一个 OutputStream:

    byte[] data;
    try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
      output.write("HelloWrold!".getBytes());
      data = output.toByteArray();
    }
    System.out.println(new String(data, "UTF-8"));
1
2
3
4
5
6

由于 OutputStream 里没有 toByteArray 方法,因此这里就为了简单,没有用 OutputStream 接受变量

运行结果:

$ javac IODemo6ByteOutputStream2.java  
$ java IODemo6ByteOutputStream2  
HelloWrold!
1
2
3

ByteArrayOutputStream 实际上是把一个 byte[] 数组在内存中变成一个 OutputStream,虽然实际应用不多,但测试的时候,可以用它来构造一个 OutputStream。

同时操作多个 AutoCloseable 资源时,在 try(resource) { ... } 语句中可以同时写出多个资源,用 ; 隔开。例如,同时读写两个文件:

// 读取input.txt,写入output.txt:
try (InputStream input = new FileInputStream("input.txt");
     OutputStream output = new FileOutputStream("output.txt"))
{
    input.transferTo(output); // transferTo的作用是复制文件
}
1
2
3
4
5
6

# 小结

Java 标准库的 java.io.OutputStream 定义了所有输出流的超类:

  • FileOutputStream 实现了文件流输出;
  • ByteArrayOutputStream 在内存中模拟一个字节流输出。

某些情况下需要手动调用 OutputStream 的 flush() 方法来强制输出缓冲区。

总是使用 try(resource) 来保证 OutputStream 正确关闭。

上次更新: 2024/10/1 15:49:31
InputStream
Decorator 模式

← InputStream Decorator 模式→

最近更新
01
新闻合订本 2025-10
10-31
02
2025 年 10 月记
10-30
03
用 AI 批量优化思源笔记排版
10-15
更多文章>
Theme by Vdoing | Copyright © 2022-2025 | 粤 ICP 备 2022067627 号 -1 | 粤公网安备 44011302003646 号 | 点击查看十年之约
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式