基于本文回答
0
评论

讲讲 Java中的节点流(Node Stream)和处理流(Processing Stream)?

在Java的I/O(输入/输出)流体系中,节点流(Node Stream)处理流(Processing Stream)是两个非常核心的概念。它们的设计体现了面向对象设计中的装饰器模式(Decorator Pattern)

下面我们用通俗易懂的语言,结合代码和图解,来彻底搞懂这两个概念。


一、 节点流(Node Stream)

1. 什么是节点流?
节点流是直接与数据源或目的地相连的流。这个“数据源”或“目的地”可以是文件、内存、网络连接等。
你可以把它想象成直接插在水源(如水井、水库)上的水管

2. 特点:

  • 直接与特定物理介质(如磁盘文件、内存数组、网络端口)关联。
  • 其构造方法的参数通常是物理介质的路径、文件对象或内存数组
  • 只提供最基本的读写功能(通常是按字节或字符逐个读写),性能较低。

3. 常用节点流分类:

介质类型 输入节点流(读) 输出节点流(写)
文件 (File) FileInputStream / FileReader FileOutputStream / FileWriter
内存数组 (Array) ByteArrayInputStream / CharArrayReader ByteArrayOutputStream / CharArrayWriter
管道 (Thread Pipe) PipedInputStream / PipedReader PipedOutputStream / PipedWriter

二、 处理流(Processing Stream / Wrapper Stream)

1. 什么是处理流?
处理流(也叫包装流)不能直接连接到数据源,它必须建立在已存在的流(节点流或另一个处理流)之上
你可以把它想象成接在水管上的过滤器、加热器或水表。它不负责直接去水井里取水,而是对流经它的水进行加工(过滤、加热、计量)。

2. 特点:

  • 不能独立存在,构造方法的参数必须是另一个流对象(通常是 InputStream / OutputStream / Reader / Writer 的子类)。
  • 它的目的是消除不同节点流的实现差异,并提供更强大、更便捷的读写功能(如缓冲、格式化、对象序列化、字符集转换)。
  • 使用了装饰器模式,使得多个处理流可以嵌套使用。

3. 常用处理流分类:

功能类型 输入处理流 输出处理流 作用
缓冲流 (Buffered) BufferedInputStream / BufferedReader BufferedOutputStream / BufferedWriter 减少I/O次数,提高读写效率(极常用)
转换流 (Bridge) InputStreamReader OutputStreamWriter 字节流转字符流,解决乱码/字符集问题
数据流 (Data) DataInputStream DataOutputStream 支持直接读写Java基本数据类型(int, double等)
对象流 (Object) ObjectInputStream ObjectOutputStream 实现对象的序列化与反序列化
打印流 (Print) - PrintStream / PrintWriter 方便格式化输出(如 System.out

三、 核心区别对比

对比维度 节点流 (Node Stream) 处理流 (Processing Stream)
连接对象 直接连接到数据源(文件、内存等) 连接在已有的流之上(包装其他流)
构造方法参数 文件路径、File 对象、内存数组等 其他 Stream / Reader / Writer 对象
主要职责 实现物理读写操作(最底层) 提高效率(如缓冲)或增加功能(如序列化)
举例 FileInputStream("a.txt") BufferedInputStream(new FileInputStream("a.txt"))

四、 代码实战:如何配合使用?

在实际开发中,我们通常将一个节点流作为底座,然后套上一个或多个处理流

示例:带缓冲的高效文件读取

java
import java.io.*;

public class StreamDemo {
    public static void main(String[] args) {
        // 1. 创建节点流:直接指向文件(水管直接插在井里)
        try (FileInputStream fis = new FileInputStream("test.txt");
             
             // 2. 创建处理流(缓冲流):包装节点流(套上一个加热器,提供缓存,提高效率)
             BufferedInputStream bis = new BufferedInputStream(fis);
             
             // 3. 创建处理流(数据流):包装缓冲流(再套一个水表,可以直接读特定类型数据)
             DataInputStream dis = new DataInputStream(bis)) {
            
            // 读取数据
            int temp;
            while ((temp = dis.read()) != -1) {
                System.out.print((char) temp);
            }
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

关于资源关闭(Close)的注意点:

在上面的代码中,我们使用了 Java 7 的 try-with-resources 语法。如果手动关闭流,只需要关闭最外层的处理流即可
因为最外层处理流的 close() 方法内部会自动调用它所包装的底层流的 close() 方法。


五、 为什么要这样设计?(装饰器模式的妙处)

如果 Java 不区分“节点流”和“处理流”,我们要实现“带缓冲的文件读取”和“带缓冲的内存读取”,就需要写:

  • BufferedFileInputStream
  • BufferedByteArrayInputStream

如果要实现“支持数据类型的网络流读取”,又需要写:

  • DataSocketInputStream

这会导致类爆炸(类的数量急剧膨胀,且代码大量重复)。

通过将流分为节点流处理流(装饰器模式):

  1. 节点流只关注“从哪里读/写到哪里”(File, Memory, Socket)。
  2. 处理流只关注“如何加工”(Buffering, Encryption, Serialization)。

两两自由组合:

  • BufferedInputStream + FileInputStream = 带缓冲的文件读。
  • BufferedInputStream + ByteArrayInputStream = 带缓冲的内存读。
  • ObjectInputStream + FileInputStream = 从文件反序列化对象。

极大地提高了代码的灵活性和可复用性。

右滑查看面试常问