InputStream 是所有字节输入流的抽象基类。它的核心使命非常纯粹和底层:从数据源(文件、网络连接、内存数组等)中,一次一个地读取原始的字节数据。
byte),即0到255之间的整数。InputStream 对数据的内容 一无所知。它不理解什么是字符、文本、图片或视频。在它眼中,所有数据都只是连续的字节序列。这使得它成为处理任何类型二进制数据的基础。我们以最常用、最基础的 FileInputStream 为例来展开。
InputStream
public abstract int read() throws IOException;。read() 方法解读:
int 类型。如果成功读取,这个 int 的值在 0 到 255 之间。FileInputStream
new FileInputStream("file.txt") 时,JVM会通过 JNI (Java Native Interface) 调用操作系统的底层函数(如 open())来打开指定的文件。FileInputStream 内部会保存这个句柄。fileInputStream.read() 时,会再次通过JNI调用操作系统的 read() 函数,并传入文件描述符。操作系统会从硬盘驱动器读取数据,先放入内核缓冲区,再复制到你的Java程序内存中。FileInputStream?直接调用 fileInputStream.read() 从文件中一个字节一个字节地读取,效率 极其低下。
原因:每一次 read() 调用,都会触发一次从Java程序(用户态)到操作系统内核(内核态)的切换,这被称为 “系统调用(System Call)”。系统调用涉及上下文切换,有相当大的性能开销。想象一下,为了读取1MB的文件,你需要进行超过一百万次这样昂贵的系统调用!
BufferedInputStream 的缓冲原理为了解决上述瓶颈,Java I/O 采用了经典的设计模式——装饰器模式(Decorator Pattern),BufferedInputStream 就是为此而生的。
结构:BufferedInputStream 包装(“装饰”)了另一个 InputStream(如 FileInputStream)。
// 推荐的用法
InputStream in = new BufferedInputStream(new FileInputStream("file.txt"));
工作流程(核心!):
new BufferedInputStream(...) 会在内部创建一个 默认大小为 8192 字节(8KB)的字节数组 作为其私有的内部 缓冲区(buffer)。private byte buf[]; // 内部的字节数组缓冲区read()]
in.read()。BufferedInputStream 检查自己的内部缓冲区,发现是空的。FileInputStream 请求1个字节,而是调用 fileInputStream.read(buf, 0, 8192),尝试 一次性从文件中读取 8KB 的数据,填满自己的整个内部缓冲区。BufferedInputStream 从缓冲区的第一个位置 (buf[0]) 取出1个字节,返回给你的代码。read()]
in.read()。BufferedInputStream 再次检查内部缓冲区,发现里面还有 8191 个字节。FileInputStream,更不需要麻烦操作系统和硬盘。它只是简单地从内部的 byte[] 数组的下一个位置 (buf[1], buf[2], ...) 取出一个字节并返回。