跳转到内容

Java BufferedInputStream

来自代码酷

模板:Note

Java BufferedInputStream[编辑 | 编辑源代码]

BufferedInputStream是Java I/O库中提供的一个带有缓冲功能的输入流类,通过减少底层系统的实际读取操作次数来提高I/O效率。它继承自`FilterInputStream`,属于装饰器模式(Decorator Pattern)的典型应用。

核心原理[编辑 | 编辑源代码]

flowchart LR A[应用程序] -->|read| B(BufferedInputStream) B -->|批量读取| C[底层InputStream] C -->|填充缓冲区| B B -->|从缓冲区提供数据| A

数学上,设单次系统调用耗时为tsys,缓冲区大小为n字节,则读取m字节数据的理论耗时从mtsys降低至m/ntsys

构造函数[编辑 | 编辑源代码]

提供两种主要构造方式:

// 方式1:使用默认缓冲区大小(通常8192字节)
InputStream fis = new FileInputStream("test.txt");
BufferedInputStream bis = new BufferedInputStream(fis);

// 方式2:自定义缓冲区大小
BufferedInputStream bisCustom = new BufferedInputStream(fis, 16384); // 16KB缓冲区

核心方法[编辑 | 编辑源代码]

方法 描述
`int read()` 读取单个字节,返回-1表示EOF
`int read(byte[] b, int off, int len)` 读取到字节数组的指定位置
`long skip(long n)` 跳过指定字节数
`void mark(int readlimit)` 设置标记点
`void reset()` 返回到标记点
`boolean markSupported()` 是否支持标记(BufferedInputStream返回true)

性能对比示例[编辑 | 编辑源代码]

测试读取100MB文件的耗时差异:

public class PerformanceTest {
    static void readWithoutBuffer() throws IOException {
        try (InputStream is = new FileInputStream("largefile.bin")) {
            long start = System.nanoTime();
            while (is.read() != -1); // 逐字节读取
            System.out.printf("无缓冲耗时: %.2fms\n", (System.nanoTime()-start)/1e6);
        }
    }

    static void readWithBuffer() throws IOException {
        try (InputStream is = new BufferedInputStream(new FileInputStream("largefile.bin"))) {
            long start = System.nanoTime();
            while (is.read() != -1); // 通过缓冲读取
            System.out.printf("缓冲耗时: %.2fms\n", (System.nanoTime()-start)/1e6);
        }
    }
}

典型输出结果:

无缓冲耗时: 4520.17ms
缓冲耗时: 320.45ms

实际应用场景[编辑 | 编辑源代码]

场景1:大文件逐行处理[编辑 | 编辑源代码]

try (BufferedReader br = new BufferedReader(
        new InputStreamReader(
            new BufferedInputStream(
                new FileInputStream("huge_log.txt"))))) {
    
    br.lines().forEach(line -> {
        // 处理每行日志
        if(line.contains("ERROR")) {
            System.out.println("发现错误: " + line);
        }
    });
}

场景2:网络资源下载[编辑 | 编辑源代码]

URL url = new URL("https://example.com/large.zip");
try (BufferedInputStream in = new BufferedInputStream(url.openStream());
     FileOutputStream out = new FileOutputStream("local_copy.zip")) {
    
    byte[] buffer = new byte[8192];
    int bytesRead;
    while ((bytesRead = in.read(buffer)) != -1) {
        out.write(buffer, 0, bytesRead);
    }
}

高级技巧[编辑 | 编辑源代码]

缓冲区大小优化[编辑 | 编辑源代码]

缓冲区大小应考虑:

  • 文件平均大小(小文件适合较小缓冲区)
  • 可用内存(通常8KB-64KB为宜)
  • 存储介质特性(SSD可适当减小缓冲区)

与NIO结合使用[编辑 | 编辑源代码]

Java NIO的`ByteBuffer`可进一步提升性能:

try (FileChannel channel = FileChannel.open(Paths.get("data.bin"))) {
    ByteBuffer buffer = ByteBuffer.allocateDirect(8192); // 直接缓冲区
    while (channel.read(buffer) != -1) {
        buffer.flip();
        // 处理数据...
        buffer.clear();
    }
}

常见问题[编辑 | 编辑源代码]

Q: 为什么读取完数据后需要关闭流?
A: 关闭流会释放系统资源(如文件句柄),且会强制刷新缓冲区。使用try-with-resources可自动处理。

Q: 缓冲区大小是否越大越好?
A: 过大的缓冲区会导致:①内存浪费 ②延迟首次数据可用时间 ③可能触发GC

Q: 如何复用缓冲区?
A: 可创建全局缓冲区池(高级用法):

class BufferPool {
    private static final ConcurrentLinkedQueue<byte[]> pool = new ConcurrentLinkedQueue<>();
    
    static byte[] getBuffer(int size) {
        byte[] buf = pool.poll();
        return buf != null && buf.length >= size ? buf : new byte[size];
    }
    
    static void returnBuffer(byte[] buf) {
        if(buf != null) pool.offer(buf);
    }
}

最佳实践[编辑 | 编辑源代码]

1. 始终使用try-with-resources确保流关闭 2. 对敏感数据读取后清空缓冲区:

   Arrays.fill(buffer, (byte)0); // 清除内存中的敏感数据

3. 考虑使用`BufferedInputStream`包装所有`InputStream`实例(除非确定不需要缓冲)

页面模块:Message box/ambox.css没有内容。