Java的InputStream是所有字节输入流的抽象基类,位于java.io包中,提供了从数据源(如文件、网络连接、内存等)读取字节数据的基本接口。本文将详细解析InputStream的核心概念、常用方法、设计模式及源码实现。
一、核心概念
1. 作用
InputStream是Java IO体系中字节输入流的顶层抽象,定义了读取字节数据的标准方法,所有具体的字节输入流类(如FileInputStream、ByteArrayInputStream、SocketInputStream等)都继承自它。
2. 数据源类型
InputStream的数据源可以是:
- 文件(
FileInputStream) - 内存(
ByteArrayInputStream) - 网络连接(
Socket.getInputStream()) - 管道(
PipedInputStream) - 其他流(如
BufferedInputStream包装其他流)
二、常用方法
1. 核心读取方法
// 读取下一个字节(0-255),返回-1表示流结束
public abstract int read() throws IOException;
// 读取数据到字节数组b,返回实际读取的字节数
public int read(byte b[]) throws IOException;
// 读取len个字节到数组b的off位置开始,返回实际读取的字节数
public int read(byte b[], int off, int len) throws IOException;
2. 其他重要方法
// 跳过n个字节,返回实际跳过的字节数
public long skip(long n) throws IOException;
// 返回可以读取的字节数估计值
public int available() throws IOException;
// 关闭流,释放资源
public void close() throws IOException;
// 标记当前位置,配合reset()使用
public synchronized void mark(int readlimit);
// 重置到上次mark()的位置
public synchronized void reset() throws IOException;
// 判断是否支持mark/reset功能
public boolean markSupported();
三、源码解析
1. 抽象方法 read()
public abstract int read() throws IOException;
- 作用:读取下一个字节数据,返回值为
0-255的整数,若返回-1表示流结束。 - 实现要求:所有子类必须实现此方法,它是
InputStream最核心的方法。
2. 批量读取方法 read(byte[] b)
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
- 作用:将数据读取到字节数组
b中,调用了read(b, 0, b.length)的重载方法。
3. 核心重载方法 read(byte[] b, int off, int len)
public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int c = read(); // 调用抽象方法read()
if (c == -1) {
return -1;
}
b[off] = (byte)c;
int i = 1;
try {
for (; i < len ; i++) {
c = read(); // 循环调用read()
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
// 异常处理
}
return i;
}
- 实现逻辑:
- 检查参数合法性。
- 调用
read()读取第一个字节。 - 循环调用
read()将数据填充到数组b中,直到读取len个字节或流结束。
- 性能问题:直接使用此方法效率较低,因为每次只读取一个字节,建议使用
BufferedInputStream包装以提高性能。
4. skip() 方法实现
public long skip(long n) throws IOException {
long remaining = n;
int nr;
if (n <= 0) {
return 0;
}
int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
byte[] skipBuffer = new byte[size];
while (remaining > 0) {
nr = read(skipBuffer, 0, (int)Math.min(size, remaining));
if (nr < 0) {
break;
}
remaining -= nr;
}
return n - remaining;
}
- 实现逻辑:
- 创建一个临时缓冲区
skipBuffer。 - 循环读取数据到缓冲区,直到跳过指定的字节数
n。
- 创建一个临时缓冲区
5. available() 方法
public int available() throws IOException {
return 0;
}
- 默认实现:返回
0,表示无法提前知道可用字节数。 - 子类重写:如
FileInputStream会返回文件剩余未读的字节数。
6. close() 方法
public void close() throws IOException {}
- 默认实现:空方法,因为抽象类无需释放资源。
- 子类重写:如
FileInputStream会关闭文件句柄,SocketInputStream会关闭网络连接。
四、设计模式
1. 模板方法模式
InputStream定义了核心方法的骨架(如read(byte[])调用read()),具体实现由子类完成,符合模板方法模式。
2. 装饰器模式
FilterInputStream及其子类(如BufferedInputStream、DataInputStream)通过包装其他InputStream实现功能增强,符合装饰器模式。
五、常见子类
1. FileInputStream
从文件读取数据,直接关联底层文件描述符。
2. ByteArrayInputStream
从内存字节数组读取数据,无需进行IO操作,适用于测试和数据处理。
3. BufferedInputStream
通过缓冲区提高读取性能,减少实际IO次数。
4. DataInputStream
用于读取基本数据类型(如int、double),需配合DataOutputStream使用。
5. ObjectInputStream
用于反序列化对象,实现Java对象的持久化。
六、使用示例
1. 读取文件内容
try (InputStream is = new FileInputStream("test.txt")) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
System.out.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
e.printStackTrace();
}
2. 使用缓冲流提高性能
try (InputStream is = new FileInputStream("test.txt");
BufferedInputStream bis = new BufferedInputStream(is)) {
int data;
while ((data = bis.read()) != -1) {
// 处理数据
}
} catch (IOException e) {
e.printStackTrace();
}
七、注意事项
- 资源管理:
InputStream使用完毕后必须调用close()关闭,建议使用try-with-resources语句自动关闭。 - 性能优化:直接使用
read()单字节读取效率低,建议使用BufferedInputStream或批量读取方法。 - 阻塞特性:传统IO是阻塞的,若需非阻塞IO,可使用
java.nio包中的FileChannel或SocketChannel。
总结
InputStream作为Java IO体系的基础组件,定义了字节输入流的标准接口,通过抽象方法和模板模式为子类提供了统一的操作框架。理解其设计思想和源码实现,有助于高效使用各种具体流类,并在需要时自定义扩展。
2676

被折叠的 条评论
为什么被折叠?



