程序员scholar 程序员scholar
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
npm

(进入注册为作者充电)

  • JavaIO流

    • 初识 Java IO 流
    • 文件操作
    • 字节流
    • 字符流
    • 缓冲流
      • 缓冲流
        • 1. 构造方法
        • 2. 缓冲流与普通流效率对比测试
        • 2.1 不使用缓冲流复制大文件
        • 2.2 使用缓冲流复制大文件
        • 3. 字符缓冲流特有方法
        • 3.1 BufferedReader的readLine方法
        • 3.2 BufferedWriter的newLine方法
        • 6.4 流的关闭顺序
    • 节点流和处理流的区别
    • IO流 其他内容
  • IO流
  • JavaIO流
scholar
2024-08-22
目录

缓冲流

# 缓冲流

缓冲流,也称为高效流,是对四大基本的文件流(FileInputStream、FileOutputStream、FileReader、FileWriter)的增强,主要通过设置内置的缓冲区来提高读写数据的效率。缓冲流根据处理的数据类型可以分为字节缓冲流和字符缓冲流。

  • 字节缓冲流:BufferedInputStream,BufferedOutputStream
  • 字符缓冲流:BufferedReader,BufferedWriter

缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。

# 1. 构造方法

  • public BufferedInputStream(InputStream in) :创建一个 新的缓冲输入流。
  • public BufferedOutputStream(OutputStream out): 创建一个新的缓冲输出流。

构造举例,代码如下:

// 使用FileInputStream创建字节缓冲输入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bis.txt"));
// 使用FileOutputStream创建字节缓冲输出流
BufferedOutputStream bos = new BufferedOutputStream(new FischolarutputStream("bos.txt"));
1
2
3
4
  • public BufferedReader(Reader in) :创建一个 新的缓冲输入流。
  • public BufferedWriter(Writer out) : 创建一个新的缓冲输出流。

构造举例,代码如下:

// 使用FileReader创建字符缓冲输入流
BufferedReader br = new BufferedReader(new FileReader("br.txt"));
// 使用FileWriter创建字符缓冲输出流
BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));
1
2
3
4

# 2. 缓冲流与普通流效率对比测试

查询API,缓冲流读写方法与基本的流是一致的,我们通过复制大文件(375MB),测试它的效率。

本示例通过复制一个大文件(比如375MB的文件),来比较使用普通的字节流和使用缓冲流在处理大量数据时的效率差异。

# 2.1 不使用缓冲流复制大文件

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class BufferedIONoBuffer {
    public static void main(String[] args) throws IOException {
        long startTime = System.currentTimeMillis(); // 记录开始时间
        
        FileInputStream fis = new FileInputStream("jdk8.exe"); // 创建文件输入流
        FileOutputStream fos = new FileOutputStream("copy_no_buffer.exe"); // 创建文件输出流
        
        byte[] buffer = new byte[1024]; // 设置一个缓冲区
        int len;
        while ((len = fis.read(buffer)) != -1) {
            fos.write(buffer, 0, len); // 读取并写入数据
        }
        
        fos.close(); // 关闭输出流
        fis.close(); // 关闭输入流
        
        long endTime = System.currentTimeMillis(); // 记录结束时间
        System.out.println("不使用缓冲流复制文件所需时间: " + (endTime - startTime) + " 毫秒");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

image-20240320085638495

在这个示例中,我们通过FileInputStream和FileOutputStream进行数据的读取和写入操作,每次读取1KB的数据后即写入文件,直至文件全部复制完成,在不使用缓冲流的情况下花费了870毫秒。

# 2.2 使用缓冲流复制大文件

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class BufferedIOUseBuffer {
    public static void main(String[] args) throws IOException {
        long startTime = System.currentTimeMillis(); // 记录开始时间
        
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("jdk8.exe")); // 创建缓冲输入流
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy_use_buffer.exe")); // 创建缓冲输出流
        
        byte[] buffer = new byte[1024]; // 设置一个缓冲区
        int len;
        while ((len = bis.read(buffer)) != -1) {
            bos.write(buffer, 0, len); // 读取并写入数据
        }
        
        bos.close(); // 关闭输出流
        bis.close(); // 关闭输入流
        
        long endTime = System.currentTimeMillis(); // 记录结束时间
        System.out.println("使用缓冲流复制文件所需时间: " + (endTime - startTime) + " 毫秒");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

image-20240320085745912

在这个示例中,我们通过BufferedInputStream和BufferedOutputStream来创建缓冲流,使用同样大小的缓冲区进行数据的读取和写入操作,花费了256毫秒。

对比结论

通过对比两种方法复制相同文件所需的时间,可以明显看到使用缓冲流(即高效流)进行文件复制的效率远高于直接使用基本的文件流。这主要得益于缓冲流内部使用的缓冲区技术,减少了对磁盘I/O操作的次数,从而显著提高了处理大量数据时的性能。

# 3. 字符缓冲流特有方法

字符缓冲流提供了一些特有的方法,使得读写文本文件时更加方便。下面通过两个示例,分别展示BufferedReader和BufferedWriter的特有方法的使用。

# 3.1 BufferedReader的readLine方法

readLine()方法可以非常方便地读取文本文件中的每一行数据。此方法会自动处理换行符,不将换行符包含在返回的字符串中。当读取到文件末尾时,返回null。

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class BufferedIOLine {
    public static void main(String[] args) {
        // 使用BufferedReader读取文本文件中的每一行
        try (BufferedReader br = new BufferedReader(new FileReader("in.txt"))) {
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line); // 打印读取的每一行
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

image-20240320090459208

# 3.2 BufferedWriter的newLine方法

newLine()方法用于在文本文件中写入一个新的行分隔符,行分隔符的具体字符序列由系统属性定义(如Windows中是\r\n,Linux中是\n)。这样就不需要关心不同平台的行分隔符差异。

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class BufferedIOLine {
    public static void main(String[] args) {
        // 使用BufferedWriter在文本文件中写入数据并换行
        try (BufferedWriter bw = new BufferedWriter(new FileWriter("out.txt"))) {
            bw.write("程"); // 写入单个字符
            bw.newLine();  // 写入换行符
            bw.write("序");
            bw.newLine();
            bw.write("员");
            bw.newLine(); // 再次写入换行符
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

image-20240320090800633

通过上述两个示例,我们可以看到BufferedReader的readLine方法和BufferedWriter的newLine方法的使用,极大地简化了文本文件的读写操作。这些特有方法是字符缓冲流相比普通字符流的优势之一。

# 6.4 流的关闭顺序

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class IOCloseDemo {
    public static void main(String[] args) {
        FileWriter fw = null;
        BufferedWriter bw = null;
        try {
            // 创建FileWriter对象,指向"d:/1.txt"
            fw = new FileWriter("d:/1.txt");
            // 创建BufferedWriter对象,包装FileWriter对象,提高写入效率
            bw = new BufferedWriter(fw);

            // 使用BufferedWriter对象写入字符串"hello"
            bw.write("hello");

            /*
             * 正确的关闭顺序是先关闭外层流(BufferedWriter),再关闭内层流(FileWriter)。
             * 关闭外层流同时会将缓冲区内容刷新到文件,然后再关闭内层流,确保数据的完整性。
             */
            bw.close();  // 先关闭BufferedWriter,自动刷新缓冲区内容到文件
            fw.close();  // 再关闭FileWriter,释放与文件的连接资源

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            /*
             * 在finally块中再次确保所有流都被关闭。
             * 这是一种额外的预防措施,以防try块中的代码发生异常导致流没有正确关闭。
             */
            try {
                if (bw != null) {
                    bw.close();
                }
                if (fw != null) {
                    fw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

关闭顺序的原则和解释

  • 外层流先关闭:首先关闭外层流(如BufferedWriter),因为关闭外层流在关闭前会自动刷新缓冲区的内容到文件中,确保数据的完整性。如果先关闭内层流(如FileWriter),那么外层流的关闭动作会尝试刷新缓冲区到已经关闭的内层流,导致IOException。
  • 内层流后关闭:在外层流关闭之后,再关闭内层流。内层流负责与文件系统的直接交互,关闭它会释放系统资源。
  • finally中再次检查关闭:在finally代码块中再次进行流的关闭操作,是一种防御式编程的做法。即使正常逻辑中已经关闭了流,但如果在关闭之前发生了异常,可能会导致流没有正确关闭。在finally中检查并关闭流,可以确保无论是否发生异常,资源都能被正确释放。
编辑此页 (opens new window)
上次更新: 2024/12/28, 18:32:08
字符流
节点流和处理流的区别

← 字符流 节点流和处理流的区别→

Theme by Vdoing | Copyright © 2019-2025 程序员scholar
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式