【Java】IO流:字节流 字符流 缓冲流

接续上文,在这篇文章将继续介绍在Java中关于文件操作的一些内容

【Java】文件操作


文章目录

  • 一、“流”的概念
    • 1.“流”的分类
      • 1.1输入流和输出流
      • 1.2字节流和字符流
    • ==字节和字符的区别?==
    • ==为什么要有字符流?==
      • 1.3节点流和处理流
    • ==字符流自带缓冲区,为什么还要用字符缓冲流?==
    • 2.“流”的特性
    • 3.“流”的分类
  • 二、Stream流(字节流)
    • 1.InputStream流
      • 版本一(无参)read()
      • 版本二 read(byte[] b)
      • 版本三 read(byte[] b,int offset,int len)
    • 2.OutPutStream流
  • 三、Stream流(字符流)
    • reader
    • writer
  • 四、 scanner

一、“流”的概念

“流”是一个抽象的概念,是对输入输出设备的一种抽象理解,在Java中,对数据的输入输出都是以“流”的的方式进行的。“流”具有方向性,输入流、输出流是相对的。当程序需要从数据源中读入数据的时候就会开启一个输入流,相反,写出数据到某个数据源目的地的时候也会开启一个输出流。数据源可以是文件、内存或者网络等
在这里插入图片描述

1.“流”的分类

“流”序列中的数据可以是未经加工的原始二进制数据,也可以是经过一定编码处理后符合某种格式的特定数据,因此java中的“流”分为以下三种流:

  1. 按数据流的方向:输入流、输出流
  2. 按处理数据单位:字节流、字符流
  3. 按功能:节点流,处理流

1.1输入流和输出流

“流”具有方向性,输入流、输出流是相对的
输入与输出是相对于应用程序而言的,比如文件读写,读取文件是输入流,写文件是输出流,这点很容易搞反。
在这里插入图片描述

1.2字节流和字符流

字节流:数据流中的最小的数据单元是字节,一次读入读出8位二进制;
字符流:数据流中的最小的数据单元是字符,一次读入读出16位二进制,java中的字符是Unicode编码,一个字符占用两个字节。

字节和字符的区别?

  • 存储方式:

字节(byte):字节是计算机存储和通信的基本单位。在Java中,一个字节由8位(bit)组成,可以表示256种不同的状态,其取值范围是-128到127(对于byte类型)。
字符(char):字符用于表示文本信息。在Java中,一个字符使用Unicode编码,占用2个字节(16位)的空间。因此,Java中的char类型可以表示65536种不同的字符。

  • 表示范围:

由于字节只有8位,其表示范围相对较小,只能表示-128到127之间的整数,或者0到255之间的无符号整数。
字符类型则能表示更多的字符,包括各种文字、符号等。由于使用了Unicode编码,Java中的char类型可以表示世界上几乎所有的字符。

为什么要有字符流?

在UTF8 编码中,“爱吃南瓜的北瓜”对应的字节如下
在这里插入图片描述
如果使用字节流处理中文,如果一次读写一个字符对应的字节数就不会有问题,一旦将一个字符对应的字节分裂开来,就会出现乱码了。为了更方便地处理中文这些字符,Java就推出了字符流。

  • 用途:

字节主要用于处理二进制数据、图像、音频、视频等非文本信息,或者在网络通信中传输数据。
字符则主要用于处理文本信息,如字符串、文件名、用户输入等。
在Java中,经常需要将字节和字符进行转换。例如,当我们从文件或网络读取数据时,通常得到的是字节流,而我们需要将其转换为字符流以便进行文本处理。这时,可以使用Java提供的解码器(Decoder)将字节转换为字符。反之,当需要将文本信息写入文件或发送到网络时,需要将字符转换为字节,这时可以使用编码器(Encoder)。

  • 区别
  1. 字节流一般用来处理图像、视频、音频、PPT、Word等类型的文件。字符流一般用于处理纯文本类型的文件,如TXT文件等,但不能处理图像视频等非文本文件。用一句话说就是:字节流可以处理一切文件,而字符流只能处理纯文本文件。
  2. 字节流本身没有缓冲区,缓冲字节流相对于字节流,效率提升非常高。而字符流本身就带有缓冲区,缓冲字符流相对于字符流效率提升就不是那么大了。

1.3节点流和处理流

  • 节点流:节点流可以从一个特定的数据源读写数据,如FileInputStream ,FileOutputStream ,FileReader ,FileWrite

在这里插入图片描述

  • 处理流:对一个已存在的流的链接和封装,通过对数据进行处理为程序提供功能强大、灵活的读写功能,例如BufferedInputStream(缓冲字节流)
    在这里插入图片描述

在这里插入图片描述

在诸多处理流中,有一个非常重要,那就是缓冲流。

我们知道,程序与磁盘的交互相对于内存运算是很慢的,容易成为程序的性能瓶颈。减少程序与磁盘的交互,是提升程序效率一种有效手段。缓冲流,就应用这种思路:普通流每次读写一个字节,而缓冲流在内存中设置一个缓存区,缓冲区先存储足够的待操作数据后,再与内存或磁盘进行交互。这样,在总数据量不变的情况下,通过提高每次交互的数据量,减少了交互次数。

在这里插入图片描述

联想一下生活中的例子,我们搬砖的时候,一块一块地往车上装肯定是很低效的。我们可以使用一个小推车,先把砖装到小推车上,再把这小推车推到车前,把砖装到车上。这个例子中,小推车可以视为缓冲区,小推车的存在,减少了我们装车次数,从而提高了效率。、

在这里插入图片描述

需要注意的是,缓冲流效率一定高吗?不一定,某些情形下,缓冲流效率反而更低

字符流自带缓冲区,为什么还要用字符缓冲流?

尽管字符流已经具备了缓冲的功能,但字符缓冲流(BufferedReader 和 BufferedWriter)仍然有其自身的优势和用途:

缓冲区大小可控:字符缓冲流提供了更大的缓冲区,可以指定缓冲区的大小。较大的缓冲区可以一次性读取或写入更多的字符数据,减少对底层I/O的频繁访问,提高读写效率。

提供了更方便的读写方法:字符缓冲流提供了一些便捷的方法,如 readLine() 方法可以一次读取一行数据,而不需要一个字符一个字符地读取。newLine() 方法可以写入一个平台特定的换行符,而不需要手动处理不同操作系统的换行符。

支持预读取和回滚:字符缓冲流具有 mark() 和 reset() 方法,可以在读取过程中进行标记(mark)并在需要时回滚(reset),方便进行预读取和回溯操作。

支持写入自动刷新:字符缓冲流提供了 flush() 方法,用于手动刷新缓冲区,并将缓冲区中的数据强制写入底层的输出流。此外,可以通过设置缓冲区的大小和自动刷新策略(如自动换行符)来控制写入时的刷新。

2.“流”的特性

一般来说关于流的特性有下面几点:

  1. 先进先出:最先写入输出流的数据最先被输入流读取到。
  2. 顺序存取:可以一个接一个地往流中写入一串字节,读出时也将按写入顺序读取一串字节,不能随机访问中间的数据。(RandomAccessFile除外)
  3. 只读或只写:每个流只能是输入流或输出流的一种,不能同时具备两个功能,输入流只能进行读操作,对输出流只能进行写操作。在一个数据传输通道中,如果既要写入数据,又要读取数据,则要分别提供两个流。

3.“流”的分类

“流”存在于Java.io包中,主要包含四种基本的类,InputStream、OutputStream、Reader及Writer类,它们分别处理字节流和字符流:

输入\输出字节流字符流
输入InputStreamReader
输出OutputStreamWriter

二、Stream流(字节流)

1.InputStream流

在这里插入图片描述

修饰符及返回值类型⽅法签名说明
intread()读取⼀个字节的数据,返回 -1 代表已经完全读完了
intread(byte[] b)最多读取 b.length 字节的数据到 b中,返回实际读到的数量;-1 代表以及读完了
intread(byte[] b, int off, int len)最多读取 len - off 字节的数据到 b中,放在从 off 开始,返回实际读到的数量;-1 代表以及读完了
voidclose()关闭字节流

在这里插入图片描述
这里InputStream是一个抽象类,不能实例化
因为它不仅仅对应的是硬盘的文件,也可以对应网卡,也可以对应控制台,也可以对应蓝牙设备
这里我们是对文件进行操作,所以我们使用系统提供对应的api来进行实例
这里使用FileInputStream

签名说明
FileInputStream(File file)利⽤ File 构造⽂件输⼊流
FileInputStream(String name)利⽤⽂件路径构造⽂件输⼊流

版本一(无参)read()

版本一(无参类型)读取的是字节类型,那么为什么返回值的类型却是int呢???

我们点开read()的源码

在这里插入图片描述
手动翻译一下

从输入流中读取下一个字节的数据。返回值byte为int型,取值范围为0到255。如果由于到达流的末尾而没有可用的字节,则返回值-1。此方法将一直阻塞,直到输入数据可用、检测到流的结尾或抛出异常。子类必须提供此方法的实现。返回:数据的下一个字节,如果到达流的结尾则返回-1。抛出:IOException -如果I/O错误发生。

此处返回int
1)为了有额外的余地来表示“到达末尾”-1这样的情况
2)确保读到的数据都是正数
   原则上来说,字节这样的概念,本应该是无符号的
   但是byte类型,本身是有符号的。
   此处使用int就可以确保读出来的字节都是正数,按照“无符号”来处理了.
3)为什么不用short?
  因为short是两个字节,int是四个字节
  随着计算机技术的发展,空间的存储成本会越来越低的,
  CPU的不断发展,每次读取的数据也会越来越长
  对于32位的CPU来说,一次读取四个字节的数据
  也就是说在计算机内部,如果是short也会转换成int来处理
  对于64位的CPU来说,一次就会读取八个字节的数据
  所以short的使用会逐步的减少,int来代替,
 public static void main(String[] args) {
        try (InputStream inputStream = new FileInputStream("D:/hello.txt")){
             while (true){
                int b = inputStream.read();

                 if (b == -1){
                     break;
                 }
                 System.out.printf("0x%x ",b);
             }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

版本二 read(byte[] b)

    public static void main(String[] args) {
        try (InputStream inputStream = new FileInputStream("D:/hello.txt")){
            byte[] bytes = new byte[1024];

            int len;
            while (true){
                len = inputStream.read(bytes);
                if (len == -1){
                    break;
                }
                for (int i = 0; i < len; i++) {
                    System.out.printf("0x%x ", bytes[i]);
                }
            }
        }  catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

这两种版本的访问速度谁更快呢?

版本二速度快,
访问硬盘是低效操作,IO次数越多,整体速度就越慢.

版本三 read(byte[] b,int offset,int len)

一次读取一部分,放置到指定位置

在网络协议中,一个报文就是由 报头 和 载荷 组成的
我们就可以用版本三来读取

2.OutPutStream流

修饰符及返回值类型⽅法签名说明
voidwrite(int b)写⼊要给字节的数据
voidwrite(byte[] b)将 b 这个字符数组中的数据全部写⼊ os 中
intwrite(byte[] b, int off, int len)将 b 这个字符数组中从 off 开始的数据写⼊ os 中,⼀共写 len 个
voidclose()关闭字节流
voidflush()重要:我们知道 I/O 的速度是很慢的,所以,⼤多的 OutputStream为了减少设备操作的次数,在写数据的时候都会将数据先暂时写⼊内存的⼀个指定区域⾥,直到该区域满了或者其他指定条件时才真正将数据写⼊设备中,这个区域⼀般称为缓冲区。但造成⼀个结果,就是我们写的数据,很可能会遗留⼀部分在缓冲区中。需要在最后或者合适的位置,调⽤ flush(刷新)操作,将数据刷到设备中。

OutputStream 同样只是⼀个抽象类,要使⽤还需要具体的实现类。我们现在还是只关⼼写⼊⽂件中,所以使⽤ FileOutputStream

同read一样,也是三个版本
在这里插入图片描述

 public static void main(String[] args) {
        try (OutputStream outputStream = new FileOutputStream("D:/hello.txt",true)) {
            outputStream.write(97);
            outputStream.write(98);
            outputStream.write(99);
            outputStream.write(100);
            outputStream.write(101);
            outputStream.write(102);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

这里的参数中多了一个Boolean类型
这个是打开了"追加写"的意思
这个功能是在对文件内容进行写入时,不会清空上次文件中的内存
会在文件的原有内容中继续写入,
在这里插入图片描述

为什么要将InputStream OutputStream流的创建写入到try() {} catch 里的小括号内呢?

这是因为我们在创建流后,在流使用后,需要对流进行关闭,在写入到try后面的小括号内时,我们就可以不必手动的来进行流的关闭,减少一些情况下我们忘记关闭流这一操作带来的风险,
这一语法被称作 try–with–resources。
但并非所有的类都可以放到try()的括号里,这个类必须实现Closeable接口
我们点开InputStream的源码
在这里插入图片描述
可以看到,InputStream流实现了closeable接口。

三、Stream流(字符流)

reader

在这里插入图片描述
点开read()的源码
在这里插入图片描述
手动翻译一下

读取单个字符。此方法将阻塞,直到字符可用、发生I/O错误或到达流的末尾。想要支持有效的单字符输入的子类应该覆盖这个方法。返回:读取的字符,取值范围为0到65535 (0x00-0xffff)的整数,如果到达流的末尾则为-1。抛出:IOException -如果发生I/O错误

取值范围为0到65535 (0x00-0xffff)也就是读取两个字节

public static void main(String[] args) {
        try(Reader reader = new FileReader("D:/hello.txt")){
               while (true){
                   int c = reader.read();

                   if (c == -1){
                       break;
                   }
                   char ch = (char)c;
                   System.out.printf("%c",ch);
               }
        }catch (IOException e){
            e.printStackTrace();
        }

在这里插入图片描述

那么不出意外的话,意外就要来了
Java中的char是两个字节,而汉字确实三个字节
但是read()确可以正常读取,这是为什么呢?

在文件中的原始数据,是三个字节一个字符
read在读取操作时,能够识别文件时UTF8编码方式
读的是三个字节,返回成一个char的时候,把UTF8编码方式,转换成了Unicode,在Unicode中一个汉字是两个字节的
链接: Java中一个汉字究竟占几个字节?

在Java内部,
char用的是Unicode
String里面默认是UTF8

那么有小可爱就可能会提出疑问了
那直接用char来接收read的返回值不就可以了,还可以省去转换这一步

这样做肯定有这样做的道理
char只能表示两个字节
文件末尾的-1怎么表示呢?

writer

在这里插入图片描述
writer也存在“追加写”
在这里插入图片描述

四、 scanner

Scanner scanner = new Scanner(System.in);

你是否好奇Sacnner里的System.in到底是什么
我们点开in的源码
在这里插入图片描述


以上就是本文所有内容,如果对你有帮助的话,点赞收藏支持一下吧!💞💞💞

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux——Centos7安装RabbitMQ】 RabbitMQ无法连接

到这一步是基本已经装好了&#xff0c;现在是在开放端口&#xff0c;我这个报错是因为我的防火墙是处于关闭状态&#xff0c;所以在开放端口时会报防火墙为运行&#xff0c;把防火墙打开&#xff0c;在开放端口&#xff0c;就可以访问到了 重启防火墙&#xff1a; systemctl …

【无标题】基于GIS、Python机器学习技术的地质灾害风险评价、易发性分析与信息化建库及灾后重建中的实践技术

理解地质灾害形成机理与成灾模式&#xff1b;从空间数据处理、信息化指标空间数据库构建、致灾因子提取&#xff0c;空间分析、危险性评价与制图分析等方面掌握GIS在灾害危险性评价中的方法&#xff1b;运用地质灾害危险性评价原理和技术方法 原文链接&#xff1a;基于GIS、Py…

DeepSeek API文档:创建对话补全的指南

DeepSeek平台不仅提供了一个用户友好的聊天界面&#xff0c;还为开发者提供了强大的API接口&#xff0c;使他们能够创建和集成智能对话补全功能。以下是关于如何使用DeepSeek API创建对话补全的详细介绍。 DeepSeek API概述 DeepSeek的API允许开发者通过编程方式与DeepSeek的…

应用软件安全保证措施方案书

系统安全保证措施方案—word原件 软件全套资料进主页获取或者本文末个人名片直接获取。

【文章转载】ChatGPT 提示词十级技巧: 从新手到专家

学习了微博网友宝玉xp老师《ChatGPT 提示词十级技巧: 从新手到专家》 个人学习要点&#xff1a; 1、关于提示中避免使用否定句&#xff0c;播主说&#xff1a;“没有人能准确解释为什么&#xff0c;但大语言模型在你告诉它去做某事时&#xff0c;表现似乎比你让它不做某事时更…

识货小程序逆向

声明 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01;wx a15018601872&#xff0c;x30184483x…

Java进阶07集合(续)

Java进阶07 集合&#xff08;续&#xff09; 一、数据结构&#xff08;树&#xff09; 1、关于树 1.1 相关概念 节点&#xff1a;树中每个单独的分支 节点的度&#xff1a;每个节点的子节点数量 树高&#xff1a;树的总层数 根节点&#xff1a;最顶层节点 左子节点&…

6层板学习笔记2

说明:笔记基于6层全志H3消费电子0.65MM间距BGA 67、多层板的电源建议直接大面积铺铜,不建议走线,铺铜充分满足其载流能力 68、凡亿推荐表层1OZ的铜厚线宽20MIL能承载1A的电流,内层0.5OZ的铜厚线宽为40MIL能承载1A的电流,过孔直径20MIL(0.5MM)能承载1A左右的电流,实际设…

typescript的入门到吐槽:看了typescript,发现前端真的卷,

typescript TypeScript 是一种由微软开发的自由和开源的编程语言。它是 JavaScript 的一个超集&#xff0c;而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程。 TypeScript 与 JavaScript 的区别 其实就是对JavaScript的封装&#xff0c;把一个弱类型语言封…

remmina无法连接远程桌面,Remmina无法连接远程桌面的原因与解决办法

在解决Remmina无法连接远程桌面的问题时&#xff0c;我们需要考虑多种可能的原因&#xff0c;并采取相应的解决办法。以下是一些常见的原因及其对应的解决方案&#xff1a; 1、网络问题 原因&#xff1a;不稳定的网络连接或中断可能导致无法建立远程桌面连接。 解决办法&#x…

MySQL数据库---增删查改汇总

前言 欢迎来到我的博客 个人主页:北岭敲键盘的荒漠猫-CSDN博客 本文着重整理MySQL数据库增删查改功能 主要是整理语法 争取做到要用什么语法 可以快速找到复制粘贴 增添语法 INSERT into tab(列名,列名,列名) values(内容,内容,内容); 插入一行数据 INSERT into tab(列名,…

Kubernetes最小单元Pod介绍及配置

1.1 Pod介绍 Pod是Kubernetes中的一个基本构建块&#xff0c;它是一个逻辑主机&#xff0c;用于托管一个或多个容器。 Pod中的容器共享网络和存储资源&#xff0c;并且通常作为一个单元一起调度和管理。 Pod为容器提供了一个共享的环境&#xff0c;使得容器之间可以方便地通信…

Android进阶之路 - 静态会员进度条

年后这个新版本加入了VIP模块&#xff0c;有幸正好由我来负责&#xff0c;可以再积累一下这方面的知识。 那段时间看了一本书&#xff0c;书中说到初级码农的特性之一就是完全集中于某些功能&#xff0c;忽略了了很多成长机会&#xff0c;所以重复性劳作带来的成长值有限&#…

基于51单片机的智能台灯proteus仿真设计( proteus仿真+程序+原理图+报告+讲解视频)

基于51单片机的红外光敏检测智能台灯控制系统仿真( proteus仿真程序原理图报告讲解视频&#xff09; 1.主要功能&#xff1a; 基于51单片机的红外检测光照检测智能台灯仿真设计 1、检测光照强度并显示在数码管上。 2、具备红外检测人体功能。 3、灯光控制模式分为自动模式…

抓取Google时被屏蔽怎么办?如何避免?

在当今数字化时代&#xff0c;数据采集和网络爬取已成为许多企业和个人必不可少的业务活动。对于爬取搜索引擎数据&#xff0c;特别是Google&#xff0c;使用代理IP是常见的手段。然而&#xff0c;使用代理抓取Google并不是一件轻松的事情&#xff0c;有许多常见的误区可能会导…

vue 语法2

【5】条件渲染和列表渲染 &#xff08;1&#xff09;条件渲染v-if v-else-if v-else 条件渲染根据表达式的真假值来渲染不同的元素或组件。 v-if&#xff1a;当表达式的值为真时&#xff0c;渲染该元素或组件。 v-else-if&#xff1a;当前面的 v-if 或 v-else-if 的表达式为假…

【C++】STL — vector的接口讲解 +详细模拟实现

前言: 本章我们将学习STL中另一个重要的类模板vector… vector是表示可变大小数组的序列容器。就像数组一样&#xff0c;vector也采用的连续存储空间来存储元素。但是又不像数组&#xff0c;它的大小是可以动态改变的本质讲&#xff0c;vector使用动态分配数组来存储它的元素v…

智慧公厕的核心技术详解:物联网、云计算、大数据、自动化控制

公共厕所是城市的重要组成部分&#xff0c;而智慧公厕的建设和管理正成为城市发展的重要方向。智慧公厕的核心技术即是物联网、云计算、大数据和自动化控制。下面将以智慧公厕源头实力厂家广州中期科技有限公司&#xff0c;大量精品案例项目现场实景实图实例&#xff0c;详细介…

Sealos急速部署生产用k8s集群

最近一段时间部署k8s全部使用sealos了&#xff0c;整体使用感觉良好&#xff0c;基本没有什么坑。推荐给大家。 使用 Sealos&#xff0c;可以安装一个不包含任何组件的裸 Kubernetes 集群。 最大的好处是提供 99 年证书&#xff0c;用到我跑路是足够了。不用像之前kubeadm安装…

【计算机科学速成课】笔记一

文章目录 写在前面1.计算机的早期历史2.电子计算机3.布尔运算和逻辑门4.二进制5.算术逻辑单元-ALU6.寄存器和内存 写在前面 所有的一切源于这样一个网站——CS自学指南。 这是新手小白入门计算机科学必要了解的知识——【计算机科学速成课】[40集全/精校] - Crash Course Comp…
最新文章