欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 社会 > I O知识框架

I O知识框架

2025/10/8 14:22:15 来源:https://blog.csdn.net/2405_88415278/article/details/146136091  浏览:    关键词:I O知识框架

I/O知识框架

初始I/O

IO,即in和out,也就是输入和输出,指应用程序和外部设备之间的数据传递,常见的外部设备包括文件、管道、网络连接。

Java 中是通过流处理IO 的,那么什么是流?

流(Stream),是一个抽象的概念,是指一连串的数据(字符或字节),是以先进先出的方式发送信息的通道。

当程序需要读取数据的时候,就会开启一个通向数据源的流,这个数据源可以是文件,内存,或是网络连接。类似的,当程序需要写入数据的时候,就会开启一个通向目的地的流。这时候你就可以想象数据好像在这其中“流”动一样。

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

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

传输方式

传输方式有两种,字节和字符,那首先得搞明白字节和字符有什么区别,对吧?

字节(byte)是计算机中用来表示存储容量的一个计量单位,通常情况下,一个字节有 8 位(bit)。

字符(char)可以是计算机中使用的字母、数字、和符号,比如说 A 1 $ 这些。

通常来说,一个字母或者一个字符占用一个字节,一个汉字占用两个字节。

具体还要看字符编码,比如说在 UTF-8 编码下,一个英文字母(不分大小写)为一个字节,一个中文汉字为三个字节;在 Unicode 编码中,一个英文字母为一个字节,一个中文汉字为两个字节。

字节流用来处理二进制文件,比如说图片啊、MP3 啊、视频啊。

字符流用来处理文本文件,文本文件可以看作是一种特殊的二进制文件,只不过经过了编码,便于人们阅读。

换句话说就是,字节流可以处理一切文件,而字符流只能处理文本。

虽然 IO 类很多,但核心的就是 4 个抽象类:InputStream、OutputStream、Reader、Writer。

虽然 IO 类的方法也很多,但核心的也就 2 个:read 和 write。

InputStream 类

  • int read():读取数据
  • int read(byte b[], int off, int len):从第 off 位置开始读,读取 len 长度的字节,然后放入数组 b 中
  • long skip(long n):跳过指定个数的字节
  • int available():返回可读的字节数
  • void close():关闭流,释放资源

OutputStream 类

  • void write(int b): 写入一个字节,虽然参数是一个 int 类型,但只有低 8 位才会写入,高 24 位会舍弃(这块后面再讲)
  • void write(byte b[], int off, int len): 将数组 b 中的从 off 位置开始,长度为 len 的字节写入
  • void flush(): 强制刷新,将缓冲区的数据写入
  • void close():关闭流

Reader 类

  • int read():读取单个字符
  • int read(char cbuf[], int off, int len):从第 off 位置开始读,读取 len 长度的字符,然后放入数组 b 中
  • long skip(long n):跳过指定个数的字符
  • int ready():是否可以读了
  • void close():关闭流,释放资源

Writer 类

  • void write(int c): 写入一个字符
  • void write( char cbuf[], int off, int len): 将数组 cbuf 中的从 off 位置开始,长度为 len 的字符写入
  • void flush(): 强制刷新,将缓冲区的数据写入
  • void close():关闭流

理解了上面这些方法,基本上 IO 的灵魂也就全部掌握了。

字节流和字符流的区别:

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

以写文件为例,我们查看字符流的源码,发现确实有利用到缓冲区:

// 声明一个 char 类型的数组,用于写入输出流
private char[] writeBuffer;// 定义 writeBuffer 数组的大小,必须 >= 1
private static final int WRITE_BUFFER_SIZE = 1024;// 写入给定字符串中的一部分到输出流中
public void write(String str, int off, int len) throws IOException {// 使用 synchronized 关键字同步代码块,确保线程安全synchronized (lock) {char cbuf[];// 如果 len <= WRITE_BUFFER_SIZE,则使用 writeBuffer 数组进行写入if (len <= WRITE_BUFFER_SIZE) {// 如果 writeBuffer 为 null,则创建一个大小为 WRITE_BUFFER_SIZE 的新 char 数组if (writeBuffer == null) {writeBuffer = new char[WRITE_BUFFER_SIZE];}cbuf = writeBuffer;} else {    // 如果 len > WRITE_BUFFER_SIZE,则不永久分配非常大的缓冲区// 创建一个大小为 len 的新 char 数组cbuf = new char[len];}// 将 str 中的一部分(从 off 开始,长度为 len)拷贝到 cbuf 数组中str.getChars(off, (off + len), cbuf, 0);// 将 cbuf 数组中的数据写入输出流中write(cbuf, 0, len);}
}

操作对象划分

  • Input:将外部的数据读入内存,比如说把文件从硬盘读取到内存,从网络读取数据到内存等等
  • Output:将内存中的数据写入到外部,比如说把数据从内存写入到文件,把数据从内存输出到网络等等。

所有的程序,在执行的时候,都是在内存上进行的,一旦关机,内存中的数据就没了,那如果想要持久化,就需要把内存中的数据输出到外部,比如说文件。

文件操作算是 IO 中最典型的操作了,也是最频繁的操作。那其实你可以换个角度来思考,比如说按照 IO 的操作对象来思考,IO 就可以分类为:文件、数组、管道、基本数据类型、缓冲、打印、对象序列化/反序列化,以及转换等。

文件

文件流也就是直接操作文件的流,可以细分为字节流(FileInputStream 和 FileOuputStream)和字符流(FileReader 和 FileWriter)。

FileInputStream 的例子:

// 声明一个 int 类型的变量 b,用于存储读取到的字节
int b;
// 创建一个 FileInputStream 对象,用于读取文件 fis.txt 中的数据
FileInputStream fis1 = new FileInputStream("fis.txt");// 循环读取文件中的数据
while ((b = fis1.read()) != -1) {// 将读取到的字节转换为对应的 ASCII 字符,并输出到控制台System.out.println((char)b);
}// 关闭 FileInputStream 对象,释放资源
fis1.close();

FileOutputStream 的例子:

// 创建一个 FileOutputStream 对象,用于写入数据到文件 fos.txt 中
FileOutputStream fos = new FileOutputStream("fos.txt");// 向文件中写入数据,这里写入的是字符串 "123" 对应的字节数组
fos.write("123".getBytes());// 关闭 FileOutputStream 对象,释放资源
fos.close();

数组流

通常来说,针对文件的读写操作,使用文件流配合缓冲流就够用了,但为了提升效率,频繁地读写文件并不是太好,那么就出现了数组流,有时候也称为内存流。

// 创建一个 ByteArrayInputStream 对象,用于从字节数组中读取数据
InputStream is = new BufferedInputStream(new ByteArrayInputStream("沉默王二".getBytes(StandardCharsets.UTF_8)));// 定义一个字节数组用于存储读取到的数据
byte[] flush = new byte[1024];// 定义一个变量用于存储每次读取到的字节数
int len = 0;// 循环读取字节数组中的数据,并输出到控制台
while (-1 != (len = is.read(flush))) {// 将读取到的字节转换为对应的字符串,并输出到控制台System.out.println(new String(flush, 0, len));
}// 关闭输入流,释放资源
is.close();

ByteArrayOutputStream 的例子:

// 创建一个 ByteArrayOutputStream 对象,用于写入数据到内存缓冲区中
ByteArrayOutputStream bos = new ByteArrayOutputStream();// 定义一个字节数组用于存储要写入内存缓冲区中的数据
byte[] info = "沉默王二".getBytes();// 向内存缓冲区中写入数据,这里写入的是 info 数组中的所有字节
bos.write(info, 0, info.length);// 将内存缓冲区中的数据转换为字节数组
byte[] dest = bos.toByteArray();// 关闭 ByteArrayOutputStream 对象,释放资源
bos.close();

数组流可以用于在内存中读写数据,比如将数据存储在字节数组中进行压缩、加密、序列化等操作。它的优点是不需要创建临时文件,可以提高程序的效率。但是,数组流也有缺点,它只能存储有限的数据量,如果存储的数据量过大,会导致内存溢出

管道

Java 中的管道和 Unix/Linux 中的管道不同,在 Unix/Linux 中,不同的进程之间可以通过管道来通信,但 Java 中,通信的双方必须在同一个进程中,也就是在同一个 JVM 中,管道为线程之间的通信提供了通信能力。

一个线程通过 PipedOutputStream 写入的数据可以被另外一个线程通过相关联的 PipedInputStream 读取出来。

// 创建一个 PipedOutputStream 对象和一个 PipedInputStream 对象
final PipedOutputStream pipedOutputStream = new PipedOutputStream();
final PipedInputStream pipedInputStream = new PipedInputStream(pipedOutputStream);// 创建一个线程,向 PipedOutputStream 中写入数据
Thread thread1 = new Thread(new Runnable() {@Overridepublic void run() {try {// 将字符串 "沉默王二" 转换为字节数组,并写入到 PipedOutputStream 中pipedOutputStream.write("沉默王二".getBytes(StandardCharsets.UTF_8));// 关闭 PipedOutputStream,释放资源pipedOutputStream.close();} catch (IOException e) {e.printStackTrace();}}
});// 创建一个线程,从 PipedInputStream 中读取数据并输出到控制台
Thread thread2 = new Thread(new Runnable() {@Overridepublic void run() {try {// 定义一个字节数组用于存储读取到的数据byte[] flush = new byte[1024];// 定义一个变量用于存储每次读取到的字节数int len = 0;// 循环读取字节数组中的数据,并输出到控制台while (-1 != (len = pipedInputStream.read(flush))) {// 将读取到的字节转换为对应的字符串,并输出到控制台System.out.println(new String(flush, 0, len));}// 关闭 PipedInputStream,释放资源pipedInputStream.close();} catch (IOException e) {e.printStackTrace();}}
});// 启动线程1和线程2
thread1.start();
thread2.start();

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词