Java 输入与输出(I\O)详解

news2025/2/25 6:27:55

一、Java 输入与输出(I\O)系统概述
Java 输入/输出流(Input/Output,简称I/O)是Java语言用于读写数据的API,它提供了一系列类和接口,用于读取和写入各种类型的数据信息。
输入/输出(I/O)流:是一组有序的数据结构,它是有顺序的,有起点和终点的字节集合,是对数据传输的抽象。它将数据从一个地方带到另一个地方。

Java I/O的层次结构概述
Java的I/O流可分成低级节点流和高级的处理流。Java通过装饰器设计模式来对I/O流进行组合,可以方便地提供了缓冲、过滤、对象序列化等特殊功能,以满足各种复杂的I/O需求。Java I/O的层次结构大致可以分为以下几个层次:

最底层:基于本地操作系统的I/O操作,这是所有I/O操作的基础。Java的I/O操作最终都会转化为底层的操作系统的系统调用,以实现数据的读写功能。

抽象层:通过InputStream、OutputStream、Reader和Writer等抽象类,Java为I/O操作提供了统一的接口。这些接口屏蔽了底层操作系统的差异,使得Java程序可以在不同的操作系统上运行而无需修改。这些抽象类定义了数据读写的基本方法,如read()和write(),以及其他一些辅助方法。

装饰器层:例如,通过BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter等装饰器类,Java为IO操作提供了缓冲功能。缓冲流通过内存缓冲区来存储数据,从而减少了与底层IO设备的交互次数,提高了数据传输的效率。同时,缓冲流还提供了更为灵活的数据读写方式,如按行读取、按指定字符序列读取等。

字符集转换层:字符类输入输出流InputStreamReader和OutputStreamWriter等类提供了字符集转换功能。由于字节流(InputStream和OutputStream)是按照字节来读取和写入数据的,而字符流(Reader和Writer)则是按照字符来处理的。因此,在字节流和字符流之间需要进行字符集转换。InputStreamReader和OutputStreamWriter类就是用于实现这种转换的。它们可以将字节流转换为字符流,或者将字符流转换为字节流,以便在不同的字符集编码之间进行数据交换。

特殊功能层:Java还提供了许多用于特定场景的I/O类。这些类在底层I/O操作的基础上,添加了额外的功能以满足特定的需求。例如,CipherInputStream和CipherOutputStream类用于加密解密数据的读写操作;ZipInputStream和ZipOutputStream类用于压缩解压缩数据的读写操作;DataInputStream和DataOutputStream类用于以特定的格式(如基本数据类型和字符串)读写数据;File类则提供了对文件和目录的访问和操作功能。

Java的I/O库通过层次化的设计,为开发者提供了灵活、强大且易于使用的I/O操作功能。

二、Java 输入与输出(I\O)系统之文件和目录(File类)

Java语言自从JDK 1.0开始的输入/输出(Input/Output,简称I/O)是同步阻塞式IO(简称BIO)。它使用java.io.File类对象来管理文件和目录。

  • File类
    Java.io中的大多数类可以完成内存和磁盘的数据交换功能,但File(文件)类例外,它是唯一一个与文件本身有关的操作类。
    File类是java.io包下代表与平台无关的文件和目录,Java程序操作文件和目录,都可以通过File类来完成。但是访问文件内容本身,要通过输入/输出流。
    关于File类的详细介绍请参见上篇博客:Java的File类详解

三、Java 输入与输出(I\O)系统之输入/输出流

JDK 1.0 最初的输入/输出(I/O)是面向输入/输出流的,输入/输出流是单向非缓冲的,它只支持字节流(InputStream、OutputStream)和字符流(Reader、Writer)两种。它采用阻塞模式,在读/写数据时,如果没有可以读取/写入的数据信息时会发生阻塞,简称BIO。

Javar 输入/输出流(InputStream/OutputStream),与Jdk 8.0中引入的流(Stream)是完全不同的概念。
Java的输入/输出(I/O)流是个抽象的概念,它是对输入输出设备读写数据的抽象。输入流可以看作一个输入通道,输出流可以看作一个输出通道。
输入流和输出流则是一个相对的概念,通常以程序为参考基点,Java输入流和输出流的主要区别在于数据的流向:当程序从某个数据源读入数据的时候,就会开启一个数据输入流(InputStream),数据源可以是文件、内存或网络等等。反之,写出数据到某个数据源目的地的时候,也会开启一个数据输出流(OutputStream),这个数据源目的地也可以是文件、内存或网络等等。
输入/输出流(InputStream/OutputStream)示意图:
在这里插入图片描述

Javar 输入/输出流(InputStream/OutputStream)的分类:

  • 按照I/O流的方向:可分为输入流(只能从中读取数据,不能向其写入数据)和输出流(只能向其写入数据,不能从中读取数据)。输入流使用InpurtStream和Reader作为基类;输出流使用OutputStream和Writer作为基类。

  • 按照I/O流处理的数据单元:可分为字节流(以字节为处理单元)和字符流(以字符为处理单元);
    在这里插入图片描述

  • 按照I/O流处理功能:可分为低级的节点流和高级的装饰流(又称处理流)。
    节点流是直接从数据源读/写数据的输入输出流;
    节点流的示意图:
    在这里插入图片描述
    装饰流(又称处理流),是指用于对一个已存在的输入输出流进行装饰,装饰流在不改变数据信息的情况下添加额外的增强功能。因为装饰流不会直接连接到实际的数据源,不能够直接获取数据,所以构建一个装饰流时,需要在构造函数中传入一个节点流。装饰流可进行多层嵌套装饰。
    例如,BufferedInputStream是FileInputStream的装饰流,它为文件输入流提供缓冲功能,从而提高读取文件的效率。
    在这里插入图片描述

(一)、字节输入与输出流
字节输入与输出流
在程序设计中,程序如果要读取或写入8位bit的字节数据,应该使用字节流来处理。字节流一般用于读取或写入二进制数据,如图片、音频文件等。字节流主要用于处理“非文本数据”的应用场景。
在计算机中,无论是文本、图片、音频还是视频,所有的文件都能以二进制比特(bit,1字节为8bit)形式传输或保存。Java语言对字节输入/输出操作提供了一系列流,统称为字节流。程序需要读取数据的时候使用输入流;而当程序需要输出或保存数据的时候使用输出流。在 Java 中,字节流提供了两个抽象基类 InputStreamOutputStream,分别用于处理字节流的输入和输出。因为抽象类不能被实例化,所以实际使用的是这两个类的实现子类。

InputStream类是字节输入流的抽象基类,InputStream 类的常用子类如下所示:
字节输入流的层次结构图:
在这里插入图片描述
InputStream是字节输入流的抽象基类,InputStream的基本功能是从数据源读取数据,它定义了几个通用的方法:

  • read( ):读取一个字节并将读取的字节作为int返回。当到达输入流的结尾时,它返回-1。
  • read(byte[] b):从流中读取b的长度个字节的数据存储到b中,返回结果是读取的字节个数(当再次读时,如果返回-1说明到了输入流的结尾了,没有数据了)
  • read(byte[] b, int off, int len):从流中从off的位置开始读取len个字节的数据存储到b中,返回结果是实际读取到的字节个数(当再次读时,如果返回-1说明到了输入流的结尾,没有数据了)
  • close():关闭流,释放资源。

OutputStream类是字节输出流的抽象基类,OutputStream 类的常用子类如下所示:
字节输出流的层次结构图:
在这里插入图片描述
OutputStream是字节输出流的基类, OutputStream作为基类,给它的基类定义了几个通用的方法:

  • write(byte[] b):将b的长度个字节数据写到输出流中。
  • write(byte[] b,int off,int len):从b的off位置开始,获取len个字节数据,写到输出流中。
  • flush():刷新输出流,把数据马上写到输出流中。
  • close():关闭流,释放系统资源。

常用的字节输出流:

  • FileOutputStream是用于写文件的输出流,它除了可以使用基类定义的方法外,还实现了OutputStream的抽象方法write(int b):
    write(int b):将b转成一个字节数据,写到输出流中。
  • BufferedOutputStream像上面那个BufferedInputStream一样,都可以提高效率。它除了可以使用基类定义的方法外,它还实现了OutputStream的抽象方法write(int b):
    write(int b):将b转成一个字节数据,写到输出流中。

(二)、字符输入与输出流

字符流是为了高效处理字符而引入的。其实质是在字节流读取时,去字符表中查了指定的码点。

字节流和字符流的区别

  • 处理的数据单元不同:
    字节流:以字节(8bit)为单位,一次读入或写出是8位比特。
    字符流:以字符为单位,根据码表映射字符,一次可能读取不同个数的字节。字符流一次读入或写出至少是一个码元。根据字符编码集不同读入的字节数是不同的。例如:UTF-8码元长度是8位比特,一个字符需要1-6个码元来表示;UTF-16码元长度是16位比特,一个字符最多需要2个码元来表示。

  • 处理对象不同:字节流能处理所有类型的数据(如图片、音视频等),而字符流只能处理字符类型的数据。设备上的数据信息无论是图片、视频或者文字,它们都以二进制形式存储。二进制最终都是以字节(8bit)为数据单元进行处理的,所以计算机中的最小数据单元就是字节。因此,字节流可以处理设备上的所有数据信息。只是字节流处理字符数据会比字符流复杂一些。

应用场景: 处理纯文本数据时优先考虑使用字符流;否则一律使用字节流。

Reader类是字符输入流的抽象基类,Reader 类的常用子类如下所示:
在这里插入图片描述
Reader是字符输入流的抽象基类 ,它定义了以下几个方法:

  • read() :读取单个字符,返回结果是一个int,需要转成char;到达流的末尾时,返回-1
  • read(char[] cbuf):读取cbuf的长度个字符到cbuf这种,返回结果是读取的字符数,到达流的末尾时,返回-1
  • close() :关闭流,释放占用的系统资源。

常用的字符输入流,如下所示:

  • InputStreamReader 可以把InputStream中的字节数据流根据字符编码方式转成字符数据流。它除了可以使用基类定义的方法,它自己还实现了以下方法:
    read(char[] cbuf, int offset, int length) :从offset位置开始,读取length个字符到cbuf中,返回结果是实际读取的字符数,到达流的末尾时,返回-1
  • FileReader 可以把FileInputStream中的字节数据转成根据字符编码方式转成字符数据流。
  • BufferedReader可以把字符输入流进行封装,将数据进行缓冲,提高读取效率。它除了可以使用基类定义的方法,它自己还实现了以下方法:
    (1),read(char[] cbuf, int offset, int length) :从offset位置开始,读取length个字符到cbuf中,返回结果是实际读取的字符数,到达流的末尾时,返回-1
    (2),readLine() :读取一个文本行,以行结束符作为末尾,返回结果是读取的字符串。如果已到达流末尾,则返回 null

Writer类是字符输出流的抽象基类,Writer 类的常用子类如下所示:
在这里插入图片描述

Writer是字符输出流的抽象基类, ,它定义了以下几个方法:
write(char[] cbuf) :往输出流写入一个字符数组。
write(int c) :往输出流写入一个字符。
write(String str) :往输出流写入一串字符串。
write(String str, int off, int len) :往输出流写入字符串的一部分。
close() :关闭流,释放资源。
flush():刷新输出流,把数据马上写到输出流中。

常用的字符输出流实现类:
OutputStreamWriter可以使我们直接往流中写字符串数据,它里面会帮我们根据字符编码方式来把字符数据转成字节数据再写给输出流,它相当于一个中介/桥梁。
FileWriter与OutputStreamWriter功能类似,我们可以直接往流中写字符串数据,FileWriter内部会根据字符编码方式来把字符数据转成字节数据再写给输出流。
BufferedWriter比FileWriter还高级一点,它利用了缓冲区来提高写的效率。它还增加了一个方法:
newLine() :写入一个换行符。

四、Java 输入与输出(I\O)系统之标准输入/输出/错误流
计算机系统都有标准的输入设备和标准输出设备。普通计算机,标准输入设备通常是键盘,而标准输出设备是屏幕。Java程序经常需要从键盘上输入数据,在屏幕上输出数据,因此,Java系统预定义了三个标准输入/输出流,两个对象System.in和System.out,分别代表来自键盘的标准输入和往屏幕的标准输出,另一个标准错误输出流System.err对象也向屏幕输出。
System.in是标准输入流,它是InputStream类的实例对象。可以使用read()方法从键盘上读取字节,也可以装饰成数据流读取各种类型的数据和字符串。
System.out和System.err是标准输出流和标准错误输出流,是OutputStream实现类PrintStream类的实例。

下面是一个使用System.in输入流获取用户键盘信息的例程:

import java.io.IOException;
public class SystemInTest {
	public static void main(String[] args)  throws IOException {
		char ch;
		System.out.print("请输入一个字符,并按回车:");
		ch = (char)System.in.read();
		System.out.print("你输入的字符是:" + ch);
	}
}	//SystemInTest例程结束。

说明: 此例程只支持录入一个(次)英文字母。
上面的代码使用了标准输入对象System.in的read()方法。需要注意的是,在键盘上按下任何一个键都会被当作输入值读入,包括按下回车键(Enter)键。当按下回车键(Enter)键时,实际上发送了两个键值,一个是回车’\r’,另一个是换行’\n’。例程因为只读了一次字符信息,后两次实际上被忽略了。如果使用如下代码就能捕捉到,会出现两次换行操作。

      while (true) {
      	System.out.print("请输入一个字符,并按回车:");
      	ch = (char)System.in.read();
		System.out.print("你输入的字符是:" + ch);
    }

五、Java 输入/输出流的常用实现类
未完待续。

参考文献:

  • Java输入输出流
  • 【博客园】输入/输出-竹马今安在

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

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

相关文章

SpringBoot项目热部署-devtools

DevTools 会使用两个类加载器&#xff08;一个用于加载不变的类&#xff0c;一个用于加载可能会变化的类&#xff09;&#xff0c;每次重启只重新加载管理变化的类的加载器&#xff0c;因此会快很多 1.导入依赖 <dependency> <groupId>org.springframework.boot&l…

动态设置placeholder-class.默认搜索图标在中间获取焦点之后再左边

默认状态:placeholder和图标在中间位置 获取焦点&#xff1a;placeholder和图标在左边光标前面位置 动态设置placeholder-class <view class"search"><input type"search" class"select-input" input-align"center" v-model.…

算法题---动态规划

题目展示&#xff1a; 动态规划的题目我们一半分为5个步骤去分析&#xff0c;第一&#xff0c;状态表示&#xff1b;第二&#xff0c;动态转移方程&#xff1b;第三&#xff0c;初始化&#xff1b;第四&#xff0c;填表顺序&#xff1b;第五&#xff0c;返回值。 状态表示 这…

[权威出版|稳定检索]2024年大数据经济与公共管理国际会议(BDEPM 2024)

2024年大数据经济与公共管理国际会议 2024 International Conference on Big Data Economy and Public Management 【1】大会信息 会议名称&#xff1a;2024年大数据经济与公共管理国际会议 会议简称&#xff1a;BDEPM 2024 大会时间&#xff1a;请查看官网 大会地点&#xf…

网络协议原理

文章目录 TCP通信原理TCP与UDP的对比应用层应用层协议 --- tcp协议定制直接传递对象自定义协议现在要解决的问题业务处理 json的使用使用json进行序列化和反序列化操作 总结 TCP通信原理 tcp是面向字节流的 同时他也是面向连接的 所以TCP的服务器编写代码如图所示: 客户端的编…

Scala入门基础(10)高级函数

一.什么是高阶函数 二.map函数 三.foreach函数 四.filter函数 五.flatten函数 正文&#xff1a; 一.什么是高阶函数 高阶函数&#xff1a;是一个特殊的函数&#xff0c;特殊之处在于&#xff1a;它指使用其他函数作为参数或返回值 &#xff08;演示&#xff09; 二.map函…

maven项目打jar包之后如何指定外部配置文件运行java类

在maven项目中,常常会用到一些配置文件,一旦打成jar包之后,想要用外部的配置文件运行,怎么做呢? 一、配置文件config.ini 在maven项目中的src/main/resources目录下存放了一个配置文件config.ini。这个文件是默认的配置文件。 db.url=jdbc:mysql://localhost:3306/qyxx?u…

【JavaScript】LeetCode:66-70

文章目录 66 组合总和67 括号生成68 单词搜索69 分割回文串70 N皇后 66 组合总和 回溯sum&#xff1a;当前组合的数字和。递归终止条件&#xff1a;sum > target。收集结果条件&#xff1a;sum target&#xff0c;找到了满足条件的组合。注意&#xff1a;因为可以重复取数&…

亚洲最具影响力人物颜廷利:心理健康对身体健康的重要影响

在当代社会&#xff0c;面对疾病与痛苦&#xff0c;人们往往在西医与中医之间做出选择。21世纪世界上知名度最高的人物颜廷利教授的精辟见解指出了这两种医学体系的根本差异&#xff1a;西医以其高昂的费用&#xff0c;针对生理上的疾苦提供快速而直接的解决之道&#xff1b;相…

Python应用指南:利用高德地图API获取公交可达圈

参考文章&#xff1a;城市公交可达圈绘制方法&#xff08;一&#xff09; - 知乎 (zhihu.com) 本篇文章我们聚焦于通过公共交通出行方式&#xff08;包括公交、地铁、公交地铁的组合&#xff09;来获取一定时间内可以到达的范围。为了实现这一目标&#xff0c;我们将使用高德地…

在 Android 应用程序中实现与WebSocket 服务器的实时通信

&#x1f604;作者简介&#xff1a; 小曾同学.com,一个致力于测试开发的博主⛽️&#xff0c;主要职责&#xff1a;测试开发、CI/CD 如果文章知识点有错误的地方&#xff0c;还请大家指正&#xff0c;让我们一起学习&#xff0c;一起进步。 &#x1f60a; 座右铭&#xff1a;不…

spring-boot学习(2)

上次学习截止到拦截器 1.构建RESfun服务 PathVariable通过url路径获取url传递过来的信息 2.MyBatisPlus 第三行的mydb要改为自己的数据库名 第四&#xff0c;五行的账号密码改成自己的 MaooerScan告诉项目自己的这个MyBatisPlus是使用在哪里的&#xff0c;包名 实体类的定义…

PL/SQL Developer15和Oracle Instant Client安装配置详细图文教程

一、下载介质 1、Oracle Instant Client Oracle Instant Client Downloads | Oracle 中国 2、PL/SQL DEVELOPER PL/SQL Developer - Allround Automations Free trial - Allround Automations 二、安装介质。 1、安装plsqldev1504x64.msi。 一路默认下一步。 选择输入许可信…

实跑 YOLO V11在 OAK内部运行的效果

哈喽&#xff0c;各位OAK中国的朋友们! 大家好我是张伯生 今天&#xff0c;我想给大家演示一下最新发布的Yolo V11神经网络 下面我将演示的一个程序是&#xff1a;同时在我们的OAK相机上跑Yolo V11和RGB-D&#xff0c;也就是彩色相机和深度图的一个叠加的一个效果 RGB-D和Yo…

java_for循环

基本语法 for 关键字&#xff0c;表示循环控制for 有四要素: (1)循环变量初始化(2)循环条件(3)循环操作(4)循环变量迭代循环操作 , 这里可以有多条语句&#xff0c;也就是我们要循环执行的代码如果 循环操作(语句) 只有一条语句&#xff0c;可以省略 {}, 建议不要省略 流程图 …

电气学习知识点

文章目录 NPN和PNP输出 NPN和PNP输出 NPN和PNP&#xff08;两种不同类型的三极管&#xff09;都是集电极输出。&#xff08;集电极开路输出&#xff09; 下图b:基极、c集电极、e发射极 NPN示意图&#xff08;集电极连接负载 — 正方形&#xff09; NPN的电流流向是从集电极…

Elasticsearch设置 X-Pack认证,设置账号和密码

前言 以下Elasticsearch版本&#xff1a;7.9.3 ES自带的X-Pack密码验证&#xff1a; X-Pack是elasticsearch的一个扩展包&#xff0c;将安全&#xff0c;警告&#xff0c;监视&#xff0c;图形和报告功能捆绑在一个易于安装的软件包中&#xff0c;所以我们想要开启账号密码验证…

Scala入门基础(10.1)高阶函数2

一.reduce 二.reduceLeft-reduceRight 三.flod 四.sorter函数 五.sortWith 一.reduce 作用&#xff1a;reduce是一种集合操作&#xff0c;用于对集合中的元素进行聚合操作&#xff0c;返回一个单一的结果。它通过指定的二元操作(即取两个元素进行操作)对集合中的所有元素进…

力扣刷题-算法基础

hello各位小伙伴们,为了进行算法的学习,小编特意新开一个专题来讲解一些算法题 1.移除元素. - 力扣(LeetCode) 本题大概意思是给定一个数组和一个数val删除与val相同的元素,不要改变剩余元素的顺序,最后返回剩余元素的个数。 我们在这里使用双指针,这里的双指针并不是…

npm 加速,命令行修改国内镜像源【附带国内最新几个镜像】超简约版~

为什么要配置国内镜像源&#xff1f; npm 的官方源服务器在国外&#xff0c;对于国内开发者来说&#xff0c;下载速度可能会比较慢&#xff0c;甚至可能会出现下载失败的情况。而国内的镜像源服务器通常会对官方源的包进行同步&#xff0c;并且在国内部署&#xff0c;这样可以…