Java 流(Stream)核心知识详解

16 阅读10分钟

一、流的分类及核心概念

在 Java 中,流(Stream)是用于处理输入/输出(I/O)操作的抽象概念,本质上是数据的传输通道,可实现数据在不同设备、不同存储形式之间的传递。流的分类主要有两种维度,分别基于操作目标和处理数据类型,具体分类及说明如下:

1. 按操作目标分类

根据流所操作的目标对象不同,可将其划分为直接操作设备的流和操作其他流的包装流,二者分工明确,适用于不同场景。

  • 操作目标为设备/存储介质:此类流直接与内存、硬盘、网络等底层设备交互,是数据传输的“源头”或“目的地”,也可称为基础流。例如,ByteArrayInputStream 专门操作内存中的字节数组,数据的读取和写入均在内存中完成,无需依赖外部设备,效率极高;FileInputStream 则直接与硬盘文件交互,通过读取硬盘中的文件数据供程序使用,是文件 I/O 操作的基础流。
  • 操作目标为其他流:此类流本身不直接与底层设备交互,而是以其他流为操作对象,通过对目标流进行包装,增强其功能或扩展其能力,因此被称为包装流(也叫过滤流)。所有包装流均继承自 FilterInputStream(输入包装流)或 FilterOutputStream(输出包装流),核心作用是在原有流的基础上添加额外功能,如缓冲、校验、压缩等。例如,BufferedInputStream 为目标流添加内存缓冲区,减少底层 I/O 次数;CheckedInputStream 为目标流的数据添加校验功能,确保数据传输的准确性。

2. 按处理数据类型分类

根据流所处理的数据单位不同,可分为字节流和字符流,二者分别对应不同的数据处理场景,核心区别在于是否涉及字符编码转换。

  • 字节流:以字节(8位)为单位处理数据,适用于所有类型的数据(如文本、图片、音频、视频等二进制数据),是 Java I/O 流的基础。所有字节流均直接或间接继承自 InputStream(字节输入流顶级父类)和 OutputStream(字节输出流顶级父类),这两个类为抽象类,定义了字节流的核心操作方法(如 read()、write()),子类需实现具体逻辑。
  • 字符流:以字符(16位 Unicode 字符)为单位处理数据,仅适用于文本数据的处理,核心优势是自动处理字符编码转换,避免出现乱码问题。所有字符流均直接或间接继承自 Reader(字符输入流顶级父类)和 Writer(字符输出流顶级父类),同样为抽象类,其底层本质还是通过字节流实现,只是在字节流基础上封装了编码转换逻辑。

二、常见流的详细说明

以下为 Java 中常用的流类,涵盖基础流、包装流等类型,详细说明其功能、原理及适用场景,帮助理解不同流的使用场景和核心价值:

流类名称详细说明
ByteArrayInputStream、ByteArrayOutputStream基础字节流,核心功能是从字节数组中读取数据(ByteArrayInputStream)和向字节数组中写入数据(ByteArrayOutputStream),本质是实现流与字节数组的相互转换。由于操作均在内存中进行,无外部设备交互,效率极高,常用于无需持久化数据的场景,如第三方 SDK 数据传输、内存中临时数据处理、小数据量的缓存转换等。
FileInputStream、FileOutputStream基础字节流,专门用于文件 I/O 操作,直接与硬盘文件交互,可实现从文件读取字节数据(FileInputStream)和向文件写入字节数据(FileOutputStream)。适用于所有文件类型的操作(文本、图片、视频等),但处理文本文件时需手动处理编码转换,否则易出现乱码,通常结合字符转换流使用。
PipedInputStream、PipedOutputStream基础字节流,用于实现两个线程之间的同步数据传递,属于线程间通信的工具类。核心原理是二者作为两个线程的公共变量,PipedInputStream 内部维护一个固定大小的字节数组缓冲区,PipedOutputStream 向缓冲区写入数据,PipedInputStream 从缓冲区读取数据,从而完成线程间的数据交换。使用时需注意,读写线程需分开,避免同一线程读写导致死锁。
SequenceInputStream基础字节流,具备“流串联”功能,可将多个输入流按顺序组合成一个整体输入流,程序读取时会依次读取每个子流的数据,直至所有子流读取完毕。适用于需要合并多个流数据的场景,如将多个文件的流串联起来,一次性读取所有文件内容,无需逐个处理每个流。
FilterInputStream、FilterOutputStream包装流的顶级父类,本身仅提供对目标流的包装能力,默认情况下并未实现任何额外的过滤或增强逻辑,所有方法均直接调用目标流的对应方法。其核心价值在于为子类提供统一的包装框架,开发者可通过继承此类,重写 read()、write() 等方法,实现自定义的过滤逻辑(如数据加密、数据筛选、格式转换等)。
BufferedInputStream、BufferedOutputStream包装字节流,核心功能是为目标流提供内存缓冲区,优化 I/O 性能。以 BufferedOutputStream 为例,写入数据时并非直接写入目标设备(如硬盘),而是先存储到缓冲区(默认缓冲区大小为 8192 字节),当缓冲区满、调用 flush() 方法或关闭流时,才将缓冲区中的数据批量写入目标设备。缓冲机制可大幅减少底层设备的 I/O 操作次数(尤其是硬盘、网络等慢速设备),显著提升读写效率,广泛应用于文件操作、网络数据传输等场景。
CheckedInputStream、CheckedOutputStream包装字节流,用于对传输的数据进行校验,确保数据的完整性和准确性,支持 CRC-32、CRC-16 等校验算法。工作原理是在读取/写入数据时,同步计算数据的校验值,传输完成后通过对比校验值判断数据是否被篡改。适用于对数据一致性要求较高的场景,如文件传输、网络通信、数据持久化等。
DataInputStream、DataOutputStream包装字节流,专门用于实现基本数据类型(int、long、float、boolean 等)和字符串与字节数据的相互转换,保留数据的原始二进制格式。例如,存储整数 10000 时,无需转换为字符串“10000”(编码后需 5 个字节),而是直接以 4 个字节的二进制形式写入(对应 int 类型的底层存储),读取时再将 4 个字节还原为 int 类型。相比字符串转换方式,其优势在于无需编码/解码过程,效率更高、存储成本更低,适用于需要高效传输基本数据类型的场景(如网络通信、二进制文件读写)。
Deflater/Inflater 系列、Zip 系列、GZIP 系列、Jar 系列流均为包装字节流,核心功能是实现数据的压缩与解压,适用于需要减少数据体积的场景(如文件存储、网络传输)。其中,DeflaterInputStream/DeflaterOutputStream 基于 ZLIB 算法实现压缩,Inflater 系列对应解压;Zip 系列支持多文件压缩与解压,可生成标准 ZIP 格式文件;GZIP 系列适用于单文件压缩,生成 GZIP 格式文件;Jar 系列专门用于 JAR 文件的读写,本质是在 ZIP 格式基础上添加了 MANIFEST.MF 清单文件。
DigestInputStream、DigestOutputStream包装字节流,用于计算流数据的消息摘要,验证数据的安全性和完整性,支持 MD5、SHA-1、SHA-256 等常用摘要算法。与 Checked 系列流的区别在于,消息摘要具有不可逆性,可用于数据防篡改验证(如文件校验、密码加密存储),而 CRC 校验仅用于数据完整性校验,不具备安全性。使用时,读取/写入数据后可获取摘要值,通过对比摘要值判断数据是否被篡改。
PrintStream包装字节流,兼具输出流和格式化输出功能,可将所有基本数据类型、对象转换为字符串形式输出,默认使用系统默认编码。其核心特点是提供了 print()、println() 等方法,支持自动换行、空指针输出“null”、布尔值输出“true/false”等便捷操作,System.out 就是 PrintStream 的实例(对应控制台输出)。适用于需要格式化输出数据的场景,如日志打印、控制台交互。
InputStreamReader、OutputStreamWriter包装流(字节流与字符流的转换桥梁),核心功能是实现字节流与字符流的相互转换,解决文本文件读写的乱码问题。InputStreamReader 将字节流转换为字符流,读取字节数据时按指定编码(如 UTF-8、GBK)转换为 Unicode 字符;OutputStreamWriter 将字符流转换为字节流,写入字符数据时按指定编码转换为字节数组。与 Data 系列流的区别在于,此类基于字符编码转换,仅适用于文本数据,而 Data 系列基于二进制格式转换,适用于基本数据类型。
FileReader、FileWriter字符流,本质是 InputStreamReader、OutputStreamWriter 的简化版,默认使用系统默认编码,专门用于文本文件的字符读写。FileReader 等价于 new InputStreamReader(new FileInputStream(file)),FileWriter 等价于 new OutputStreamWriter(new FileOutputStream(file)),适用于无需自定义编码的文本文件操作,简化了字符流操作的代码。
BufferedReader、BufferedWriter包装字符流,为字符流提供内存缓冲区,优化文本读写效率,对应字节流的 Buffered 系列。BufferedReader 额外提供了 readLine() 方法,可一次性读取一行文本数据,简化了文本行读取操作;BufferedWriter 提供了 newLine() 方法,可自动适配不同系统的换行符(Windows 为 \r\n,Linux 为 \n)。适用于大量文本数据的读写场景,如日志文件解析、配置文件读写。
LineNumberReader字符流,继承自 BufferedReader,在缓冲功能的基础上新增了行号跟踪功能。可通过 setLineNumber() 方法设置起始行号,通过 getLineNumber() 方法获取当前读取的行号,适用于需要定位文本行号的场景,如代码编辑器、日志分析工具(需标注错误行号)。
CharArrayReader、CharArrayWriter字符流,对应字节流的 ByteArray 系列,核心功能是实现流与字符数组的相互转换,操作均在内存中进行,效率极高。CharArrayReader 从字符数组中读取字符数据,CharArrayWriter 向字符数组中写入字符数据,适用于内存中临时文本数据的处理,如字符串拼接、文本数据缓存。
PipedReader、PipedWriter字符流,对应字节流的 Piped 系列,用于实现两个线程之间的文本数据传递,底层通过字符缓冲区实现同步通信,避免了字节流处理文本时的编码问题。适用于线程间传递文本数据的场景,使用时需注意读写线程分离,防止死锁。
StringReader、StringWriter字符流,专门用于字符串的读写操作,本质是将字符串作为流的数据源和目的地。StringReader 从字符串中读取字符数据,StringWriter 向内部维护的 StringBuffer 中写入字符数据,最终可通过 toString() 方法获取写入的字符串。适用于需要以流的方式处理字符串的场景,如字符串解析、文本格式化,简化了字符串与流之间的转换操作。

核心注意点:1. 包装流使用后,只需关闭外层包装流,底层目标流会被自动关闭,避免资源泄露;2. 字符流仅适用于文本数据,处理二进制数据(图片、视频)需使用字节流,否则会导致数据损坏。