Explicación detallada de la tecnología de flujo IO de Java

¿Qué es IO?

Primero, veamos la explicación dada por Baidu.

La entrada/salida de E/S (Entrada/Salida) se divide en dos partes: dispositivo IO e interfaz IO.

i es escribir, la primera letra de Entrada. o es salida, la primera letra de Salida.

IO también se conoce como flujo IO, IO = flujo, su núcleo es la operación de archivos, flujos de entrada y salida de tipos de bytes y caracteres.

clasificación OI

Los flujos de E/S se dividen principalmente en dos categorías, flujos de bytes y flujos de caracteres . Según la clasificación de funciones, se puede dividir en flujo de entrada y flujo de salida .

¿caudal?

Hay tres formas de almacenar datos en la computadora, una es la memoria externa, la otra es la memoria interna y la otra es el caché. Por ejemplo, el disco duro, el disco, el disco U, etc. en la computadora son todos almacenamiento externo, hay una tarjeta de memoria en la computadora y el caché está en la CPU. La capacidad de almacenamiento de la memoria externa es la más grande, seguida de la memoria y finalmente la caché, pero la lectura de datos en la memoria externa es la más lenta, seguida de la memoria y la caché es la más rápida. Aquí hay un resumen de la lectura de datos de la memoria externa a la memoria y la escritura de datos de la memoria a la memoria externa. Para la comprensión de la memoria y la memoria externa, podemos entenderla simplemente como un contenedor, es decir, la memoria externa es un contenedor y la memoria es otro contenedor. Entonces, ¿cómo leer los datos en el contenedor de almacenamiento externo en el contenedor de almacenamiento interno y cómo almacenar los datos en el contenedor de almacenamiento interno en el almacenamiento externo?

Podemos pensar en este conjunto como una piscina. La tubería de salida de agua y la tubería de inyección de agua están conectadas a la piscina. El agua saliente es equivalente a nuestra corriente saliente. La inyección de agua es equivalente a nuestro flujo de entrada.

Archivo (clase de archivo)

En primer lugar, si necesitamos usar flujos de E/S, definitivamente debemos crear lo que llamamos un "grupo".

¿Cómo crearlo? Creamos directamente un objeto de clase File.

package IoDemo;
import java.io.*;
public class IoDemo {
    public static void main(String[] args) {
        //创建File对象
        File file  = new File("D:\\test.txt");
    }
}
复制代码

De hecho, este objeto, para decirlo sin rodeos, se utiliza para almacenar la dirección de archivo de un flujo de E/S.

Una vez que se crea el objeto, no tiene ninguna operación, la operación debe usar este objeto para llamar a los métodos.

Primero usamos el createNewFile()método para crear el archivo de nuestra ruta anterior.

file.createNewFile();
复制代码

Tenga en cuenta que al definir la ruta del archivo, puede usar "/" o "\".

Y al crear un archivo, si hay un archivo con el mismo nombre en el directorio, se sobrescribirá.

因为有时候,可能我们的路径下已经存在了相对应的同名文件,所以我们要使用exists()方法判断文件是否已经存在。

 		//创建File对象
        File file  = new File("D:\\test.txt");
        //创建文件
        try {
            //判断文件是否存在
            if(!file.exists()){
                file.createNewFile();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
复制代码

其实,File类里面还存在许多方法,用法都是可以直接调用的,作为一个合格的程序员,我们可以直接阅读相关说明而在合适的时候使用对应方法

下面列举一些常用方法。

​ ①、创建方法

    1.boolean createNewFile() 不存在返回true 存在返回false     2.boolean mkdir() 创建目录,如果上一级目录不存在,则会创建失败     3.boolean mkdirs() 创建多级目录,如果上一级目录不存在也会自动创建

  ②、删除方法

    1.boolean delete() 删除文件或目录,如果表示目录,则目录下必须为空才能删除     2.boolean deleteOnExit() 文件使用完成后删除

  ③、判断方法

    1.boolean canExecute()判断文件是否可执行     2.boolean canRead()判断文件是否可读     3.boolean canWrite() 判断文件是否可写     4.boolean exists() 判断文件或目录是否存在     5.boolean isDirectory() 判断此路径是否为一个目录     6.boolean isFile()  判断是否为一个文件     7.boolean isHidden()  判断是否为隐藏文件     8.boolean isAbsolute()判断是否是绝对路径 文件不存在也能判断

 ④、获取方法

    1.String getName() 获取此路径表示的文件或目录名称     2.String getPath() 将此路径名转换为路径名字符串     3.String getAbsolutePath() 返回此抽象路径名的绝对形式     4.String getParent()//如果没有父目录返回null     5.long lastModified()//获取最后一次修改的时间     6.long length() 返回由此抽象路径名表示的文件的长度。     7.boolean renameTo(File f) 重命名由此抽象路径名表示的文件。     8.File[] liseRoots()//获取机器盘符     9.String[] list() 返回一个字符串数组,命名由此抽象路径名表示的目录中的文件和目录。     10.String[] list(FilenameFilter filter) 返回一个字符串数组,命名由此抽象路径名表示的目录中满足指定过滤器的文件和目录。

字节流的使用

我们现在已经建好这个**“水池”了,同时还可以使用方法来获取到“水池”**的一些信息。

那接下来,我们可以试着创建一套

字节流,相当于一滴滴的水在一个管道里运输。这个管道我们可以抽象的称之为流。

我们还是先看看知乎上,某些大佬的解释。

大佬的解释

图中蓝色为主要对应部分,红色为不对应部分,黑色的虚线部分代表这些流一般需要搭配使用。从上面的图中可以看出Java IO中的字节流是非常对称的。我们来看看这些字节流中不对称的几个类。

  1. LineNumberInputStream 主要完成从流中读取数据时,会得到相应的行号,至于什么时候分行、在哪里分行是由改类主动确定的,并不是在原始中有这样一个行号。在输出部分没有对应的部分,我们完全可以自己建立一个LineNumberOutputStream,在最初写入时会有一个基准的行号,以后每次遇到换行时会在下一行添加一个行号,看起来也是可以的。好像更不入流了。

  2. PushbackInputStream 的功能是查看最后一个字节,不满意就放入缓冲区。主要用在编译器的语法、词法分析部分。输出部分的BufferedOutputStream 几乎实现相近的功能。

  3. StringBufferInputStream 已经被Deprecated,本身就不应该出现在InputStream 部分,主要因为String 应该属于字符流的范围。已经被废弃了,当然输出部分也没有必要需要它了!还允许它存在只是为了保持版本的向下兼容而已。

  4. SequenceInputStream 可以认为是一个工具类,将两个或者多个输入流当成一个输入流依次读取。完全可以从IO 包中去除,还完全不影响IO 包的结构,却让其更“纯洁”――纯洁的Decorator 模式。

  5. PrintStream 也可以认为是一个辅助工具。主要可以向其他输出流,或者FileInputStream 写入数据,本身内部实现还是带缓冲的。本质上是对其它流的综合运用的一个工具而已。一样可以踢出IO 包!System.out 和System.out 就是PrintStream 的实例。

对于不完全学透的看法

听起来有些难度哈,这些高级用法我们先不管。我们先来看看它到底怎么用的。

那有人又说了,那如果不去学完整,以后要是不会用怎么办?

答:其实只需要掌握主要方法就可以,因为你如果需要实现一个非常用的东西,你肯定是事先就需要去查阅相关资料,而日常开发中,常用的也就可以信手拈来啦。

OutputStream(字节输出流)

这个抽象类是表示输出字节流的所有类的超类。

!!!这里的输出可不是我们正常的输出,它反而想法,是将字节写入到文件,可以理解为将字节输出到文件。

看看里面有些啥常用方法。

我们先演示一下,字节输出流如何使用?

//定义一个String值
String str = "Hello World";
复制代码

等下我们利用字节流将这个String写入我们的文件里面。

等等!这里是字节流,我怎么可以直接写入**String**?

嘿嘿,我们使用String类中的getBytes()方法将String转换成字节数组。

//将String值转换成字节数组
byte[] bytes = str.getBytes();
复制代码

整个代码是这样的:

package IoDemo;
import java.io.*;
public class IoDemo {
    public static void main(String[] args) {
        //创建File对象
        File file  = new File("D:\\test.txt");
        //创建文件
        try {
            //判断文件是否存在
            if(!file.exists()){
                file.createNewFile();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        //定义一个String值
        String str = "Hello World";

        //创建字节流
        FileOutputStream fos = null;
        try {
            //将File对象(即地址)给到FileInputStream
            fos = new FileOutputStream(file);
            //将String值转换成字节数组
            byte[] bytes = str.getBytes();
            //循环将字节数组写入到文件中
            for (int i = 0; i < bytes.length; i++) {
                //将bytes写入文件
                fos.write(bytes[i]);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

复制代码

我们进我们的文件看看输出效果。

就已经写入了我们的String里面的内容了。

InputStream(字节输入流)

这里我也就不解释了,他这个输入不是将内容输入到文件,而是将文件里面的内容输入到我们的代码中。

//创建字节输入流
FileInputStream fis = null;
try {
    //将File对象(即地址)给到FileInputStream
    fis = new FileInputStream(file);
    //创建字节数组
    byte[] bytes = new byte[1024];
    //输出字节数组
    int len = 0;
    while ((len = fis.read(bytes)) != -1) {
        System.out.println(new String(bytes, 0, len));
    }
} catch (FileNotFoundException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}
复制代码

在上文,我们已经写入了一个Hello Word

我们现在这段代码,我们将会输出文件中的内容。

len = fis.read(bytes)) != -1
复制代码

我现在还是先讲解一下这一段。

我们的len是定义的一个数值。而read()方法,是为了读取到整个文件里面内容的字节长度,就像数组的lenth()一样。

!= -1是因为,如果值为-1那么,这个文件可以说是没有数据的,空的你也没必要输出。

而我们字节流呢,一般是用于读取二进制文件,如音频、图片这些,大片的文字内容还是交给字符流吧。

字符流的使用

上面的字节流,它是一个个的输出的,而我们现在的字符流,是大水管输出,一次可以运输一段。

一般可以用记事本打开的文件,我们可以看到内容不乱码的。就是文本文件,可以使用字符流。而操作二进制文件(比如图片、音频、视频)必须使用字节流。

FileWriter(字符输出流)

老规矩哈,方法自己看看。

先写入一个String试试。

package IoDemo;
import java.io.*;
public class IoDemo {
    public static void main(String[] args) {
        String str = "嘿嘿!我是字符流·········";
        //创建File对象
        File file  = new File("D:\\test.txt");
        try {
            //将str用writer写入文件
            FileWriter fw = new FileWriter(file);
            fw.write(str);
            //关闭流,字符流必须关闭流才可以输出
            fw.close();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
复制代码

写入效果如下:

实际上,写入字符串就几个步骤。

            //1.将str用writer写入文件
            FileWriter fw = new FileWriter(file);
            //2.使用write()方法写入字符串
            fw.write(str);
            //3.关闭流,字符流必须关闭流才可以输出
            fw.close();
复制代码

FileReader(字符输出流)

		//输出文件内容
        try {
            //创建输入字符流
            FileReader fr = new FileReader(file);
            //输出文件内容
            int ch = 0;
            while ((ch = fr.read()) != -1){
                System.out.print((char)ch);
            }
            //关闭流
            fr.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }	
复制代码

这里采用Char挨个字符遍历数据。

包装流

缓冲流

为什么使用缓冲流呢?

简单地说就是,写入数据更快,可以加速写入。

缓冲流,也叫高效流。 能够高效读写缓冲流,能够转换编码的转换流,能够持久化存储对象的序列化对象等等。 它是四个基本File流的增强,所以也是4个流,按照数据类型分类。 缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO读取次数,从而提高读写的效率。

package IoDemo;
import java.io.*;
public class IoDemo {
    public static void main(String[] args) {
        //创建File对象
        File file  = new File("D:\\test.txt");
        //创建缓冲流
        BufferedWriter bw = null;
        try {
            //创建FileWriter对象
            FileWriter fw = new FileWriter(file);
            //创建BufferedWriter对象
            bw = new BufferedWriter(fw);
            //写入一首诗分四次写入
            bw.write("窗前明月光,");
            bw.write("疑是地上霜。");
            bw.write("举头望明月,");
            bw.write("低头思故乡。");
            bw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

复制代码

这个很简单,自己看就可以看懂。

转换流

InputStreamReader:把字节输入流转换为字符输入流

OutputStreamWriter:把字节输出流转换为字符输出流

我就不写示范了,网上找了一段。

        //转换流实现将 a.txt 文件 复制到 b.txt 中

        //1、创建源和目标
        File srcFile = new File("io"+File.separator+"a.txt");
        File descFile = new File("io"+File.separator+"b.txt");
        //2、创建字节输入输出流对象
        InputStream in = new FileInputStream(srcFile);
        OutputStream out = new FileOutputStream(descFile);
        //3、创建转换输入输出对象
        Reader rd = new InputStreamReader(in);
        Writer wt = new OutputStreamWriter(out);
        //3、读取和写入操作
        char[] buffer = new char[10];//创建一个容量为 10 的字符数组,存储已经读取的数据
        int len = -1;//表示已经读取了多少个字符,如果是 -1,表示已经读取到文件的末尾
        while((len=rd.read(buffer))!=-1){
            wt.write(buffer, 0, len);
        }
        //4、关闭流资源
        rd.close();
        wt.close();

复制代码

扩展

然后,找相关资料时,还涨了个知识。大家可以看看。

合并流

合并流:把多个输入流合并为一个流,也叫顺序流,因为在读取的时候是先读第一个,读完了在读下面一个流。

 	    SequenceInputStream seinput = new SequenceInputStream();
        new FileInputStream("io/1.txt"), new FileInputStream("io/2.txt"));
        byte[] buffer = new byte[10];
        int len = -1;
        while((len=seinput.read(buffer))!=-1){
            System.out.println(new String(buffer,0,len));
        }
        seinput.close();
复制代码

这里先是创建了一个SequenceInputStream对象,然后new了两个FileInputStream对象,我们使用合并流读取,先使用**seinput.read方法**读取1.txt的内容,然后再对其2.txt的内容。

Supongo que te gusta

Origin juejin.im/post/7085255604104593444
Recomendado
Clasificación