「Java」一篇文章让你会用Java的io流

Java
226
0
0
2023-10-10

一、什么是 io流

流是指数据的流动,io流就是输入输出的流动。

在java中对数据的操作是用流的方式来实现的,数据不可能一瞬间就全部地从一个设备传到另一个设备,所以只能采用一点一点流动的方式。

输入流 就是 将其他地方的数据读取到程序中 输出流 就是 将程序中的数据写入到其他地方

二、io流的分类

1.四个抽象类

在 java .io中提供了最重要的四个抽象类, InputStream (字节输入流), OutputStream (字节输出流), reader (字符输入流),Writer(字符输出流)

一字符等于两字节

根据输出的数据单位,可以分为两类, 字节流 字符流

「Java」一篇文章让你会用Java的io流

还可以根据 输入或输出 ,分为 输入流 输出流

根据 实现功能的不同 ,还可以分为 节点流 处理流

节点流 就是, 从一个特定的节点读取或者写入数据 。一看到节点就感觉好深奥,其实说白了就是从一个地方读取或写入数据,而这个地方可以是数据库,控制台,文件等。

处理流 就是, 代理节点流,实现一些其他的功能 。像BufferedWriter,如果是普通的FileWriter,在执行写操作时就就只能一个字符一个字符来写,如果再套上一个BufferedWriter,就会有缓存的功能,写的操作就会更加的快速了。

2.各种实现类

「Java」一篇文章让你会用Java的io流

可能会有所遗漏,更加详细的可以查看java官方的文档。

三、如何使用

以FileInputStream和FileOutputStream为例

1.代码

 package test;
import java.io.File;
import java.io. File InputStream;
import java.io.FileOutputStream;

/**
 * @author xxj
 * io流测试
 */
public class IOTest {
    public static void main(String[] args) {
       //先打开一个文件
        File file= new File("C:UsersAdministratorDesktop test .txt");
        try{
            //先写入数据
             fileOutputStream  fileOutputStream=new FileOutputStream( file );
            fileOutputStream.write("ABCD".getBytes());
            fileOutputStream.close();
            //读取数据
             FileInputStream  fileInputStream=new  fileInputStream (file);
            int i=;
            while ((i=fileInputStream.read())!=-){
                System.out.println("通过fileInputStream读取的数据:"+i);
                System.out.println("通过fileInputStream读取的数据:"+(char)i);
                System.out.println("------");
            }
            fileInputStream.close();
        }catch (Exception e){}
    }
    }

2.输出结果

「Java」一篇文章让你会用Java的io流

因为我加了( char )所以自动做了 ASCII 码表的转换

我的桌面也生成了test.txt文件,有兴趣的拿代码去跑一下吧。

3.解释

可能有人会问,你这不对啊,你不是FileInputStream和FileOutputStream吗,怎么刚好每次读取都是一个字母呢,不是一个字节吗?

一个字节是八位,再看一下 ASCII码表 ,里面已经有上百个符号或字母了,肯定不能只用4bit来表示啦,最少也要用8bit,8bit最高可以表示256,所以这里直接读取就是单个字母来读取的。

再以FileReader和FileWriter为例

4.代码

 public class IOTest {
    public static void main(String[] args) {
        try{
            //先写入
            File file=new File("C:UsersAdministratorDesktopwriter.txt");
            FileWriter fileWriter=new FileWriter(file);
            fileWriter.write("ABC一二三");
            fileWriter.close();
            //读取
            FileReader fileReader=new FileReader(file);
            int i=;
            while ((i=fileReader.read())!=-){
                System.out.println("通过reader读取的数据:"+i);
                System.out.println("通过reader读取的数据:"+(char)i);
                System.out.println("------");
            }
            fileReader.close();
        }catch (Exception e){}
    }
    }

5. 输出结果

「Java」一篇文章让你会用Java的io流

6.解释

我有个疑问,FileReader为什么可以读字母,跟FileInputStream一样,那为什么它会刚好可以只读一个中文又只读到一个字母呢?

19968=0x4e00

writer.txt的16进制文件为

 43 e4b8 80e4 ba8c e4b8 89

根本没有4e00,那么我们再转化成 二进制

(一)19968= 100111000000000

(二)20108= 100111010001100

(三)19977= 100111000001001

0xe4b880e4ba8ce4b889=111001001011100010000000111001001011101010001100111001001011100010001001

我使用了String.replace()方法,发现e4b8 80e4 ba8c e4b8 89中并没有包含一二三的二进制。

这里就涉及到 编码 了,常用的支持中文的编码表是UTF-8,于是我将一二三拉进去查看进制转换

「Java」一篇文章让你会用Java的io流

这。真的是一套一套的。

到了这里,也不是很清楚究竟是怎么确认只读一个字节还是读一个字符

四、各种io流的特点

不同的io流使用起来是差不多的,只是细节上有些不同,具体的可以查查 百度 或者直接看每个实现类的源码,毕竟它们都是分别继承了InputStream(字节输入流),OutputStream(字节输出流),Reader(字符输入流),Writer(字符输出流),其中处理流是根据Filter···stream间接继承的。

我稍微看了一下,想FileOutputStream最后的写操作是带有native修饰符的,又要查漏补缺了。

 private native void writeBytes(byte b[], int off, int len, boolean append)
        throws IOException;

字节流和字符流的区别

  1. 字节流 一次只能操作 一个字节 字符流 可以操作 两个字节
  2. 字节流 能够处理 任何文件类型 ,而 字符流 只能处理 文本类型 的文件

1.节点流

File流

字节 :FileInputStream,FileOutputStream

字符 :FileReader,FileWriter

特点

这些都是 需要先创建一个File对象 ,然后才能使用的,而读取或写入操作都是在创建的File对象上做的。

只有一个区别, 前两个是字节流 后两个是字符流 ,如果需要 读取或写入中文 ,就需要 使用后两个

Piped流

字节 :PipedInputStream,PipedOutStream,

字符 :PipedReader,PipedWriter

一般是 多线程 之间进行通信时使用的

特点

在使用这些流时,相比于File流,就不用先创建一个File对象,只需要 跟普通的流一样创建 即可,但是在管道(Piped) 相互通信前 ,需要 使用connect方法将两个管道连接起来

Piped流不需要使用文件作为数据的载体。

字节/字符数组流

字节 :ByteArrayInputStream,ByteArrayOutputStream

字符 :CharArrayReader, Char ArrayWriter

这四个流内部都会有一个数组,使用时是 先向数组中存入数据 ,然后再 从数组中读取 将数组的数据写入 到其他地方。

特点

Byte开头的,就是只能读取或写入字节。 ByteArrayInputStream 在创建时需要 向构造方法传递一个参数 (字节数组) ByteArrayOutputStream 则在使用时,需要 调用writeTo (某个流)这两个流可以帮助我们 先将数据存储到缓存区 ,然后读取写入更加快速,开辟的缓存区是用来存储字节数组的

CharArrayReader,CharArrayWriter和ByteArrayInputStream,ByteArrayOutputStream类似,只不过Char开头的是 读写字符 ,而Byte是读写字节,用法都一样的。

2.处理流

处理流在使用的时候,是需要套上一个节点流,然后使用方法跟使用节点流差不多。

Buffered缓冲流

字节 :BufferedInputStream,BufferedOutputStream

字符 :BufferedReader,BufferedWriter

这四个流跟字节/字符流类似,它们内部也 有一个数组作为缓存区 存在。但是, 字节/字符数组流 是将数据都缓存到数组中,并 不会去刷新这个数组 ,想 读取或写入 都是 从这个数组出发的 ;而 Buffered缓冲流 ,这会 有一个不会二次改变的数组 (可以在构造方法中传入size)一旦这个数组存满了,就会将数组的数据全部读出或写入到其他地方,然后 刷新数组

字节/字符数组流 建议 一次性用完就关闭

特点

在套用了其他节点流之后,使用的方法和节点流的使用方式差不多,但是在Buffered缓冲流 写入时 还需要使用一个 flush()方法 ,将缓存内部的数据 全部写入 并且刷新缓存

转化流

字符 :InputStreamReader,OutputStreamWriter

特点

就是能够 将字节流转换成字符流 ,在创建时,需要向 构造方法 传递一个 字节流和字符集 (默认为GBK)使用的方法跟字符流的使用方法差不多。

数据流

字节 :DataInputStream,DataOutputStream

特点

数据流是能够 将数据按照不同的类型写入 读取 也要按照相应的类型读取 。类似于写入时,将数据装入一个个不同类型的盒子里,读取时要按照盒子的类型并且按照写入的顺序来读取。

打印流

字节 :PrintStream

字符 :PrintWriter

特点

这两个流并没有对应的输入流,使用这两个的方法跟其他处理流差不多。在套上一个节点流后,就可以使用 print()或println()直接写入数据

有没有觉得这两个方法很熟悉,平时我们使用System.out.println()这个方法的内部其实也是使用PrintStream来实现的。

对象流

字节 :ObjectInputStream,ObjectOutputStream

特点

这两个流可以做一件事,就是 我们要写入或读取的 数据序列化 , 序列化 就是将对象转化成二进制数据。要使用这两个流, 第一步 也是要 套上一个节点流 第二步 就是在要序列化的类上 实现Serializable接口 ,剩下的使用方法就跟普通的节点流差不多了。

五、总结

这些流真的是多,总结一下使用场景吧。

写入或读取都是 文本类型 有中文 ,就用 字符流

写入或读取其他 杂七杂八的文件类型 没有中文 ,就用 字节流

要考虑 线程 通信 ,就用 Piped流 ;要 操作文件 ,就用 File流 ;要 将数据存在内存 中,就用 字节/字符数组流 ,没有性能和功能要求,用这三个就够了。

想要 提高读写性能 ,就套个 Buffered缓冲流 ;想要对 规定数据格式 ,就套个 数据流 ;想要 使用方便或换行 ,就套个 打印流 ;想要对 对象序列化 ,就套个 对象流

——————————————————————————————

你知道的越多,不知道的就越多。

如果本文章内容有问题,请直接评论或者私信我。如果觉得我写得还不错的话,点个赞也是对我的支持哦