按照数据流方向如何划分?
-
输入流(Input Stream):从源(如文件、网络等)读取数据到程序。
-
输出流(Output Stream):将数据从程序写出到目的地(如文件、网络、控制台等)。
按处理数据单位如何划分?
-
字节流(Byte Streams):以字节为单位读写数据,主要用于处理二进制数据,如音频、图像文件等。
-
字符流(Character Streams):以字符为单位读写数据,主要用于处理文本数据
按功能如何划分?
-
节点流(Node Streams):直接与数据源或目的地相连,如 FileInputStream、FileOutputStream。
-
处理流(Processing Streams):对一个已存在的流进行包装,如缓冲流 BufferedInputStream、BufferedOutputStream。
-
管道流(Piped Streams):用于线程之间的数据传输,如PipedInputStream、PipedOutputStream。
详细了解
-
按照流的方向(输入流和输出流)的区别
-
输入流(InputStream/Reader)
-
定义和功能:输入流主要用于从外部数据源(如文件、网络连接、内存缓冲区等)读取数据到程序中。它是数据进入程序的通道,就像水通过管道流入容器一样。例如,使用
FileInputStream
可以从文件中读取字节数据,BufferedReader
可以从文件或其他字符输入源读取字符数据,并按行读取文本内容。 -
使用场景:在读取本地文件内容、接收网络传输过来的数据(如 HTTP 请求中的数据)、解析配置文件等场景中经常使用。例如,读取一个文本文件中的用户配置信息,将数据读取到程序中进行后续的处理,如加载用户的偏好设置等。
-
-
输出流(OutputStream/Writer)
-
定义和功能:输出流的作用是将程序中的数据输出到外部目标(如文件、网络连接、打印机等)。它是数据从程序流出的通道,例如,
FileOutputStream
可以将字节数据写入文件,PrintWriter
可以将字符数据(如格式化后的文本)输出到文件或者其他输出设备。 -
使用场景:在保存程序运行结果到文件(如日志文件、数据备份文件)、向网络发送响应数据(如服务器向客户端发送 HTML 页面)等场景中发挥作用。比如,将程序运行过程中的错误信息记录到日志文件中,就需要使用输出流将错误信息写入日志文件。
-
-
-
按照操作单元(字节流和字符流)的区别
-
字节流(InputStream/OutputStream)
-
定义和操作单元:字节流以字节(8 位二进制数据)为基本操作单元,它可以处理任何类型的数据,因为所有的数据在底层存储和传输时最终都是以字节形式存在的。字节流可以直接操作文件、网络连接等底层资源,对数据进行逐个字节的读取或写入。例如,
FileInputStream
和FileOutputStream
用于对文件进行字节级别的读写操作,适合处理二进制文件(如图像文件、音频文件、视频文件等),因为这些文件的内容是按照字节序列存储的。 -
性能和适用范围:字节流在处理二进制数据时效率较高,因为它不需要进行字符编码和解码的转换。但对于文本数据的处理,如果涉及字符编码(如 UTF - 8、GBK 等),就需要开发人员自己手动处理编码相关的问题,相对比较复杂。字节流的通用性强,适用于所有类型的数据读写,但对于文本内容的操作不够直观。
-
-
字符流(Reader/Writer)
-
定义和操作单元:字符流以字符为基本操作单元,字符的大小和编码方式取决于具体的字符集。字符流在内部会根据选定的字符编码(如 UTF - 8)将字节转换为字符进行处理,更适合用于处理文本数据。例如,
FileReader
和FileWriter
用于读取和写入文本文件,它们会自动按照默认或指定的字符编码将字节转换为字符或者将字符转换为字节。 -
性能和适用范围:字符流在处理文本数据时更加方便,因为它自动处理了字符编码问题,开发人员可以直接操作字符,而不需要关心字节和字符之间的转换。但是,字符流在处理二进制数据时可能会出现问题,因为它会对数据进行字符编码转换,可能会破坏二进制数据的原始结构。字符流主要适用于处理文本文件(如.txt 文件、.java 文件等)、文本形式的网络数据(如 HTTP 响应中的 HTML 文本)等。
-
-
-
按照流的角色(节点流和处理流)的区别
-
节点流(直接操作数据源的流)
-
定义和数据源连接:节点流是直接连接到数据源(如文件、网络套接字等)或者目标(如文件、网络连接的接收端等)的流,它是数据读写的起点和终点。例如,
FileInputStream
、FileOutputStream
、FileReader
和FileWriter
都是节点流,它们直接与文件进行连接,负责从文件读取数据或者将数据写入文件。 -
功能特点:节点流提供了对数据源或目标最基本的读写功能,能够直接操作数据。但是,它们的功能可能相对比较单一,在一些复杂的场景下,如需要对数据进行缓冲、转换格式、加密等操作时,就需要和处理流结合使用。
-
-
处理流(对流进行包装和处理的流)
-
定义和对流的操作:处理流是对已存在的流(节点流或者其他处理流)进行包装和处理的流,它本身不直接连接数据源或目标,而是通过包装其他流来增强流的功能。例如,
BufferedInputStream
和BufferedOutputStream
是对字节输入流和字节输出流进行缓冲处理的流,InputStreamReader
和OutputStreamWriter
是在字节流和字符流之间进行转换的处理流。 -
功能特点:处理流可以提供多种附加功能,如缓冲(减少频繁的底层读写操作,提高性能)、数据转换(如字节流和字符流之间的转换)、数据加密 / 解密(通过包装加密算法对流中的数据进行处理)等。处理流可以层层嵌套,通过组合不同的处理流来实现复杂的功能,例如,可以先使用缓冲流对节点流进行缓冲,再使用转换流将字节流转换为字符流,最后使用加密流对数据进行加密。
-
-
管道流(PipedInputStream 和 PipedOutputStream、PipedReader 和 PipedWriter)的基本概念
-
管道流用于在两个线程之间进行通信,使得一个线程的输出可以作为另一个线程的输入。它就像一个管道,将数据从一端传递到另一端。在 Java 中,有字节管道流(PipedInputStream 和 PipedOutputStream)和字符管道流(PipedReader 和 PipedWriter),分别用于字节数据和字符数据的传输。
-
-
管道流的工作原理
-
连接机制:管道流必须先进行连接才能正常工作。对于字节管道流,通过
PipedOutputStream.connect(PipedInputStream)
方法或者在PipedInputStream
的构造函数中传入PipedOutputStream
来建立连接;对于字符管道流,通过PipedWriter.connect(PipedReader)
方法或者在PipedReader
的构造函数中传入PipedWriter
来建立连接。 -
数据传输过程:一旦连接成功,一个线程可以向
PipedOutputStream
(或PipedWriter
)写入数据,另一个线程就可以从对应的PipedInputStream
(或PipedReader
)读取这些数据。写入的数据会被暂存在管道内部的缓冲区中,等待读取线程读取。例如,一个线程负责从网络接收数据并写入管道输出流,另一个线程从管道输入流读取数据并进行本地存储或其他处理。
-
-
与其他流的区别
-
使用场景不同:
-
与普通的输入 / 输出流(如文件输入 / 输出流)不同,管道流主要用于线程间通信,而不是用于和外部设备(如文件、网络连接)进行数据交互。普通输入 / 输出流是针对数据源(如文件系统、网络等)进行数据读写,管道流则是在程序内部的线程之间传递数据。
-
例如,在一个多线程的生产者 - 消费者模型中,如果生产者和消费者在同一个 JVM 进程中,就可以使用管道流来实现它们之间的数据传递。生产者线程生产的数据通过管道流发送给消费者线程,而不是将数据写入文件或网络再读取。
-
-
数据流向和关联方式不同:
-
管道流是成对出现且相互关联的,数据的流向是从输出端到输入端,并且这种流向是固定的。而其他流(如节点流和处理流)的数据流向取决于具体的操作(如从文件读取数据时,数据从文件流向程序;向文件写入数据时,数据从程序流向文件),并且它们之间的关联主要是通过包装(如处理流包装节点流)来实现功能的扩展,不是像管道流这样用于线程间的直接数据传输。
-
-
数据存储和缓冲特点不同:
-
管道流内部有一定的缓冲区来暂存数据,但这个缓冲区大小有限(默认是 1024 字节)。如果写入数据的速度超过读取数据的速度,缓冲区可能会满,此时写入线程会被阻塞;反之,如果读取数据的速度过快,缓冲区为空时,读取线程会被阻塞。与其他流相比,普通的输入 / 输出流在处理文件等数据源时,其缓冲机制可以通过缓冲流(如
BufferedInputStream
和BufferedOutputStream
)等进行更灵活的配置和控制,而且一般不会因为数据的产生和消费速度不一致而出现与线程阻塞相关的问题(除非在多线程同时操作同一个流时没有正确处理同步)。
-
-
-