Java IO流介绍以及缓冲为何能提升性能

news2024/9/21 22:05:40

        流是一种抽象概念,它代表了数据的无结构化传递。按照流的方式进行输入输出,数据被当成无结构的字节序或字符序列。从流中取得数据的操作称为提取操作,而向流中添加数据的操作称为插入操作。

        Java IO 也称为IO流,它的核心就是对系统文件的操作、和系统之间读写。IO流主要分为两大类,字节流和字符流。字节流可以处理任何类型的数据,如图片,视频等,字符流只能处理字符类型的数据。字符流是由Java虚拟机将字节转化为2个字节的Unicode字符为单位的字符而成的,所以它对多国语言支持性比较好!如果是音频文件、图片、歌曲,就用字节流好点,非要用字符流处理也行,但是可能会遇到问题或数据损坏,因为字符流可能会将二进制数据解释为字符,导致意外的结果如果是关系到中文(文本)的,用字符流好点。

        IO流的本质是数据传输,并且流是单向的。

        字节流的基本单位为字节(Byte),一个字节通常为 8 位。字节(Byte,简写为B)一般用作数据存储单位,而数据传输大多以位(bit,简写为b)为单位。 一个位代表一个0 或1(即二进制),每8 个位组成1 个字节。

数据持久化:

        所有文件的储存是都是字节(byte)的储存,在磁盘上保留的并不是文件的字符而是先把字符编码成字节,再储存这些字节到磁盘。在读取文件(特别是文本文件)时,也是一个字节一个字节地读取以形成字节序列。

        我们使用FileWriter向demo.txt中写入了“demo”这四个字符,我们写入的“demo”被编码为了“64 65 6D 6F”,字符流在输出前实际上是要完成Unicode码元序列到相应编码方式的字节序列的转换,所以它会使用内存缓冲区来存放转换后得到的字节序列,等待都转换完毕再一同写入磁盘文件中。

流关闭

流是有起点和终点的字节序列的集合,通过流可以读写设备中的数据, 这儿的设备可以是文件, 内存, ….  IO操作属于资源操作,一定要记得关闭 

示例方法:

为什么一定要close()呢?

* A:让流对象变成垃圾,这样就可以被垃圾回收器回收了

* B:通知系统去释放跟该文件相关的资源

流分类

根据数据处理的不同类型分为:字节流和字符流

字节流:OutputStream 和 InputStream 为基类。

字符流:Writer 和 Reader 为基类。

根据数据流向不同分为:输入流和输出流

输出流:OutputStream 和 Writer 为基类。

输入流:InputStream 和 Reader 为基类。

Io框架图

        虽然IO 有很多,但是最基础的四个类是InputStreamOutputStreamReaderWriter。其他的诸如文件流都是在这四个类的基础上扩展的。比如,我们经常使用的文件读写流,实际上是调用了底层系统的read()write()方法。其他;流都是上面这四类流子类,方法也是通过这两个方法衍生的。而且大部分的IO源码都是native的,也就是说它们是用C/C++写的,这是我们在其他高级语言中无法实现的。

InputStream -- 字节输入流

OutputStream -- 字节输出流

Reader -- 字符输入流

Writer -- 字符输出流

        所有以Stream结尾的流类都是字节流类, 在字节流中输出数据主要是使用OutputStream完成,输入使的是InputStream,在字符流中输出主要是使用Writer类完成,输入流主要使用Reader类完成。以Reader结尾的流类是字符输入流类, 以Writer结尾的流类是字符输出流类。输入/输出流, 是以当前程序为参照, 把数据从其他资源里面传送到内存里面,就是输入流,反之。把数据从内存传送到其他资源就是输出流。

 不同流的作用

流的性能提升

        使用缓冲区能大幅度提升流性能,什么是缓冲

缓冲:     

缓冲的作用

如果不设置缓冲的话,CPU相对于一个文件的每个字节都需要取一个存一个。设置缓冲区的话:

CPU通常会使用 DMA (将字节积攒一定量在请求cpu发送数据就是DMA方式,cpu成为被调用者,这之后cpu在执行操作将内存数据持久化到硬盘)方式去执行 I\O流 操作。CPU 将这个工作交给DMA控制器来做,自己腾出时间做其他的事,当DMA完成工作时,DMA会主动告诉CPU“操作完成”。这时,CPU接管后续工作。在此,CPU 是被动的。DMA是专门 做 I\O 与 内存 数据交换的,不仅自身效率高,也节约了CPU时间。

 缓冲区就是内存里的一块区域,把数据先存内存里,然后一次性写入,类似数据库的批量操作,显然效率比较高。还节省CPU的使用。

总结:使用缓冲处理流包装就是一堆一堆的干活,还能不用CPU多次处理数据转换,只是设置一下数据转换成功后的文件。不使用缓冲处理流包装就是CPU傻傻的一个字节一个字节循环来干活存储写入文件中,相比可见效率明显变慢  。

再简单说缓冲区的优势以文件流的写入为例,如果我们不使用缓冲区,那么每次写操作 CPU 都会和低速存储设备也就是磁盘进行交互,那么整个写入文件的速度就会受制于低速的存储设备(磁盘)。但如果使用缓冲区的话,每次写操作会先将数据保存在高速缓冲区内存上,当缓冲区的数据到达某个阈值之后,再将文件一次性写入到磁盘上。因为内存的写入速度远远大于磁盘的写入速度,所以当有了缓冲区之后,文件的写入速度就被大大提升了。

                  

 BufferedInputStream/BufferedOutputStream

BufferedReader/BufferedWriter

BufferedWriter 和 BufferedReader 为带有默认缓冲的字符输出输入流,因为有缓冲区所以很效率比没有缓冲区的很高。

默认有8192个字符的缓冲区。

br.readLine()  一次可以读一行

bw.newLine();  换行

bw.flush();使用缓冲区中的方法,将数据刷新到目的地文件中去。

bw.close();关闭缓冲区,同时关闭了fw流对象

性能测试示例
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
 
public class WriteExample {
    public static void main(String[] args) throws IOException {
        // 构建写入内容
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < 1000000; i++) {
            stringBuilder.append("ABCDEFGHIGKLMNOPQRSEUVWXYZ");
        }
        // 写入内容
        final String content = stringBuilder.toString();
        // 存放文件的目录
        final String filepath1 = "/Users/mac/Downloads/io_test/write1.txt";
        final String filepath2 = "/Users/mac/Downloads/io_test/write2.txt";
        final String filepath3 = "/Users/mac/Downloads/io_test/write3.txt";
        final String filepath4 = "/Users/mac/Downloads/io_test/write4.txt";
        final String filepath5 = "/Users/mac/Downloads/io_test/write5.txt";
        final String filepath6 = "/Users/mac/Downloads/io_test/write6.txt";
 
        // 方法一:使用 FileWriter 写文件
        long stime1 = System.currentTimeMillis();
        fileWriterTest(filepath1, content);
        long etime1 = System.currentTimeMillis();
        System.out.println("FileWriter 写入用时:" + (etime1 - stime1));
 
        // 方法二:使用 BufferedWriter 写文件
        long stime2 = System.currentTimeMillis();
        bufferedWriterTest(filepath2, content);
        long etime2 = System.currentTimeMillis();
        System.out.println("BufferedWriter 写入用时:" + (etime2 - stime2));
 
        // 方法三:使用 PrintWriter 写文件
        long stime3 = System.currentTimeMillis();
        printWriterTest(filepath3, content);
        long etime3 = System.currentTimeMillis();
        System.out.println("PrintWriterTest 写入用时:" + (etime3 - stime3));
 
        // 方法四:使用 FileOutputStream  写文件
        long stime4 = System.currentTimeMillis();
        fileOutputStreamTest(filepath4, content);
        long etime4 = System.currentTimeMillis();
        System.out.println("FileOutputStream 写入用时:" + (etime4 - stime4));
 
        // 方法五:使用 BufferedOutputStream 写文件
        long stime5 = System.currentTimeMillis();
        bufferedOutputStreamTest(filepath5, content);
        long etime5 = System.currentTimeMillis();
        System.out.println("BufferedOutputStream 写入用时:" + (etime5 - stime5));
 
        // 方法六:使用 Files 写文件
        long stime6 = System.currentTimeMillis();
        filesTest(filepath6, content);
        long etime6 = System.currentTimeMillis();
        System.out.println("Files 写入用时:" + (etime6 - stime6));
 
    }
 
    /**
     * 方法六:使用 Files 写文件
     * @param filepath 文件目录
     * @param content  待写入内容
     * @throws IOException
     */
    private static void filesTest(String filepath, String content) throws IOException {
        Files.write(Paths.get(filepath), content.getBytes());
    }
 
    /**
     * 方法五:使用 BufferedOutputStream 写文件
     * @param filepath 文件目录
     * @param content  待写入内容
     * @throws IOException
     */
    private static void bufferedOutputStreamTest(String filepath, String content) throws IOException {
        try (BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(
                new FileOutputStream(filepath))) {
            bufferedOutputStream.write(content.getBytes());
        }
    }
 
    /**
     * 方法四:使用 FileOutputStream  写文件
     * @param filepath 文件目录
     * @param content  待写入内容
     * @throws IOException
     */
    private static void fileOutputStreamTest(String filepath, String content) throws IOException {
        try (FileOutputStream fileOutputStream = new FileOutputStream(filepath)) {
            byte[] bytes = content.getBytes();
            fileOutputStream.write(bytes);
        }
    }
 
    /**
     * 方法三:使用 PrintWriter 写文件
     * @param filepath 文件目录
     * @param content  待写入内容
     * @throws IOException
     */
    private static void printWriterTest(String filepath, String content) throws IOException {
        try (PrintWriter printWriter = new PrintWriter(new FileWriter(filepath))) {
            printWriter.print(content);
        }
    }
 
    /**
     * 方法二:使用 BufferedWriter 写文件
     * @param filepath 文件目录
     * @param content  待写入内容
     * @throws IOException
     */
    private static void bufferedWriterTest(String filepath, String content) throws IOException {
        try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filepath))) {
            bufferedWriter.write(content);
        }
    }
 
    /**
     * 方法一:使用 FileWriter 写文件
     * @param filepath 文件目录
     * @param content  待写入内容
     * @throws IOException
     */
    private static void fileWriterTest(String filepath, String content) throws IOException {
        try (FileWriter fileWriter = new FileWriter(filepath)) {
            fileWriter.append(content);
        }
    }
}

在查看结果之前,我们先去对应的文件夹看看写入的文件是否正常,如下图所示

从上述结果可以看出,每种方法都正常写入了 26 MB 的数据,它们最终执行的结果如下图所示:

通过结果可以看出有缓冲区的是性能最强的 

------------------------------------------与正文内容无关------------------------------------
如果觉的文章写对各位读者老爷们有帮助的话,麻烦点赞加关注呗!小弟在这拜谢了!
如果您觉得我的文章在某些地方写的不尽人意或者写的不对,从而让你对你人生观产生颠覆(概不负责),需要斧正,麻烦在评论区不吝赐教,作者看到后会根据您的提示查阅文章进行修改,还这世间一个公理一片蓝天

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1354142.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

QT上位机开发(数据库sqlite编程)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 编写软件的时候&#xff0c;如果用户的数据比较少&#xff0c;那么用json保存是非常方便的。但是一旦数据量大了之后&#xff0c;建议还是用数据库…

计算机网络(8):因特网上的音频/视频服务

概述 计算机网络最初是为传送数据设计的。因特网 IP 层提供的 “尽最大努力交付” 服务以及每一个分组独立交付的策略&#xff0c;对传送数据信息十分合适。因特网使用的 TCP 协议可以很好地解决P层不能提供可靠交付这一问题。 音频/视频常称为多媒体信息 多媒体信息&#xff…

进程与计划任务

什么是程序&#xff1f; 程序&#xff1a;在硬盘上躺着&#xff0c;执行特定任务的一串代码 静态 进程&#xff1a;加载到内存中运行 动态 进程是程序的副本&#xff0c;进程是有生命周期&#xff0c;是硬件资源分配的最小单位 pid号可以通过pstree -p 查到 uid &…

Cytoscape3.8安装下载及安装教程

Cytoscape3.8下载链接&#xff1a;https://docs.qq.com/doc/DUmhZQ1lqTWhuSXJC 1.选中下载好的安装包右键选择“解压到 Cytoscape3.8.0”文件夹 2.打开解压好的”Cytoscape3.8.0“文件夹 3.选中“Cytoscape_3_8_0_windows_64bit.exe“右键以管理员身份运行 4.点击”Download“&…

平衡二叉树,力扣

目录 前序遍历与后续遍历 题目地址&#xff1a; 题目&#xff1a; 我们直接看题解吧&#xff1a; 审题目事例提示&#xff1a; 解题方法&#xff1a; 难度分析&#xff1a; 解题方法分析&#xff1a; 解题分析&#xff1a; 解题思路&#xff1a; 代码实现&#xff1a; 补充说明…

使用jieba库进行中文分词和去除停用词

jieba.lcut jieba.lcut()和jieba.lcut_for_search()是jieba库中的两个分词函数&#xff0c;它们的功能和参数略有不同。 jieba.lcut()方法接受三个参数&#xff1a;需要分词的字符串&#xff0c;是否使用全模式&#xff08;默认为False&#xff09;以及是否使用HMM模型&…

魔术表演Scratch-第14届蓝桥杯Scratch省赛真题第1题

1.魔术表演&#xff08;20分&#xff09; 评判标准&#xff1a; 4分&#xff1a;满足"具体要求"中的1&#xff09;&#xff1b; 8分&#xff1a;满足"具体要求"中的2&#xff09;&#xff1b; 8分&#xff0c;满足"具体要求"中的3&#xff09…

机器学习--ROC AUC

参考 机器学习-ROC曲线 - 知乎 (zhihu.com)https://zhuanlan.zhihu.com/p/347470776一文看懂ROC、AUC - 知乎 (zhihu.com)https://zhuanlan.zhihu.com/p/81202617 在了解之前&#xff0c;我们先来认识一下以下的概念 针对一个二分类问题&#xff0c;将实例分成正类(postive)或…

linux-6.0 内核存储栈全景图

linux 存储栈原图地址&#xff1a;https://www.thomas-krenn.com/en/wiki/Linux_Storage_Stack_Diagram

【Linux系统编程二十六】:线程控制与线程特性(Linux中线程库/线程创建/线程退出/线程等待)

【Linux系统编程二十六】&#xff1a;线程控制与线程特性 一.Linux线程库pthread1.线程控制块2.线程tid3.线程栈 二.线程控制1.线程创建2.线程退出3.线程等待 三.线程的特性1.独立栈2.局部存储3.线程可分离 一.Linux线程库pthread 在Linux中&#xff0c;是没有明确的线程概念的…

Sonarqube安装(Docker)

一&#xff0c;拉取相关镜像并运行 # 拉取sonarqube镜像 docker pull sonarqube:9.1.0-community在运行之前要提前安装postgres并允许&#xff0c;新建数据库名为sonar的数据库 Docker安装postgres教程 docker run -d --name sonarqube --restartalways \ -p 19000:9000 \ …

Redis 持久化—RDB

文章目录 1. 为什么需要Redis持久化&#xff1f;2. Redis持久化的几种方式3. RDB简介4. 持久化触发4.1 手动触发4.1.1 save命令4.1.2 bgsave 命令 4.2 自动触发4.2.1 save m n4.2.2 flushall4.2.3 主从同步触发 5. 配置说明6. 配置配置7. 配置配置8. RDB 文件恢复9. RDB 优缺点…

ResNet论文阅读和简单实现

论文&#xff1a;https://arxiv.org/pdf/1512.03385.pdf Deep Residual Learning for Image Recognition 本模块主要是阅读论文&#xff0c;会做简单的翻译&#xff08;至少满足我自己能看明白&#xff09;。 Introduction 由上图可见&#xff0c;在20层和56层的网络上训练的…

深信服技术认证“SCCA-C”划重点:云计算关键技术

为帮助大家更加系统化地学习云计算知识&#xff0c;高效通过云计算工程师认证&#xff0c;深信服特推出“SCCA-C认证备考秘笈”&#xff0c;共十期内容。“考试重点”内容框架&#xff0c;帮助大家快速get重点知识。 划重点来啦 *点击图片放大展示 深信服云计算认证&#xff08…

滑动窗口最大值(力扣239题)

单调递减队列&#xff1a; 在解决题目之前&#xff0c;我们先来了解一下单调递减队列&#xff0c;它其实就是在队列的基础上多加了一些限制&#xff0c;如下图&#xff1a; 要求队列中的元素必须按从大到小的顺序排列。 如果向单调递减队列中加入数字 1&#xff0c;可以直接加入…

基于ElementUI封装的下拉树选择可搜索单选多选清空功能

效果&#xff1a; 组件代码 /*** 树形下拉选择组件&#xff0c;下拉框展示树形结构&#xff0c;提供选择某节点功能&#xff0c;方便其他模块调用* author wy* date 2024-01-03 * 调用示例&#xff1a;* <tree-select * :height"400" // 下拉框中树形高度* …

智慧工厂:科技与制造融合创新之路

随着科技的迅猛发展&#xff0c;智慧工厂成为制造业领域的热门话题。智慧工厂利用先进的技术和智能化系统&#xff0c;以提高生产效率、降低成本、增强产品质量和灵活性为目标&#xff0c;正在引领着未来制造业的发展。 智慧工厂的核心是数字化和自动化生产&#xff0c;相较于传…

C语言实例_string.h库函数功能及其用法详解

一、前言 在计算机编程中&#xff0c;字符串处理是一项常见而重要的任务。C语言的string.h头文件提供了一系列函数和工具&#xff0c;用于对字符串进行操作和处理。这些函数包括字符串复制、连接、比较、查找等功能&#xff0c;为开发人员提供了强大的字符串处理能力。本文将对…

vue3中pdf打印问题处理

1 get请求参数问题 之前的请求是post得不到参数&#xff0c;今天发现的问题很奇怪&#xff0c;从前端进入网关&#xff0c;网关居然得不到参数。 前端代码 const print () > {let linkUrlStr proxy.$tool.getUrlStr(proxy.$api.invOrder.psiInvOrder.printSalOutstock,{a…

Winform中使用Websocket4Net实现Websocket客户端并定时存储接收数据到SQLite中

场景 SpringBootVue整合WebSocket实现前后端消息推送&#xff1a; SpringBootVue整合WebSocket实现前后端消息推送_websocket vue3.0 springboot 往客户端推送-CSDN博客 上面实现ws推送数据流程后&#xff0c;需要在windows上使用ws客户端定时记录收到的数据到文件中&#x…