Java IO流总结

介绍IO流之前,首先介绍File

File

我们知道,java中一切皆对象。File类就是对一个实体文件(例如磁盘上的某个文件或者文件夹)的抽象,通过File的实例对象对实体文件进行引用,然后进行一系列的操作。

  	//file1只是对"d:/info.txt"文件的抽象引用,至于文件存在与否对这句话并没有什么影响
    File file1 = new File("d:/info.txt");
    //这句话是对文件夹的引用,同样,存在与否对这句话的执行并没有影响
    File file2 = new File("d:/info");

接下来就可以利用File实例进行一系列文件属性的操作,比如

  • 文件是否存在
  • 文件创建/删除/重命名
  • 是目录还是普通文件
  • 权限查询(是否可读/写/执行)
  • 路径查询

File类的基本操作API都很简单,根据IDE下的提示,和个人需求直接使用就行了,基本没什么问题,这里就不举例子了。

注:凡是涉及到文件内容的操作,File类是无能为力的,只能通过IO流完成。
因此File类的对象一般作为IO流对象的参数,指明IO操作的作用对象。

IO流

刚开始学习IO流的时候对这个概念很是费解,为什么叫做IO流呢?现在看来,主要是因为思维没有转变过来。

平日生活当中我们向其他人索取东西往往都是直来直往的,比如我买一瓶水,店家直接递给我“一瓶”水,而不是说给你个水管,让你自己抽一瓶带走。

但是如果提起南水北调,由丹江口调水入北京,这很自然就和“流”对应起来了,因为源头的水无法一下子全部传输到北方,只能通过“流”的形式一点点传输。

计算机中也是一样,无论是文件还是网络,进行IO操作(输入/输出)只能采用渐进式的数据传输,现在看来IO流的叫法实在是太贴切不过了!

IO流的分类

从三个角度对IO流进行分类,大家先了解一下即可,后面会详细介绍很多常用的具体的流对象

按照操作数据单位不同

  • 字节流
  • 字符流

首先需要知道的是字节流是万能的,因为计算机中一切文件底层皆是字节。

对于文本文件这种,我们可以采用字符流进行传输,但是对于视频、图片这样的二进制文件只能采用字节流的方式传输。

按照流向不同

  • 输入流
  • 输出流

这里需要注意的是,我们是站在程序的角度对流向进行命名的。从网络或磁盘中读取数据到程序中,我们成为输入;从程序向网络或磁盘中写入数据,我们称之为输出。

按照流的角色

  • 节点流
  • 处理流

这两个概念我们平时接触到的不是特别多,但是搞懂了这两个概念对理清IO流的规律有很大的帮助。

节点流:程序与文件(或者网络)之间直接端对端的传输过程中使用的流。这么说有点抽象,举个例子,假如我们有一个空桶和一个装满水的水箱,我们装一根水管用来从水箱向空桶疏水,在不纠结这到底是输入流还是输出流的情况下(根本不重要),我们称这个水管就是节点流。

这个时候有好事者认为水流太慢了,于是在原来的水管的基础上又加上了一层其他水管(我们假设加上这层水管后真的使流速变快了,别杠。。。我说快了就是快了),那么我们称后来加上的这个水管为处理流。

在java.io中,我们常用的节点流一共有四个,分别是FileInputStream,FileOutputStream,FileReader,FileWriter

其他的你都可以看作是处理流!怎么样,是不是特别简单!!

当然了,上面的四个类如果从其他角度分类的话又会属于不同的流类型,比如FileInputStream属于字节流,同时又是输入流。IO流中的类其实规律性很强,仅仅根据名字你就能知道他们的类型

IO流的4个基本抽象类

  • InputStream(字节输入流)
  • OutputStream(字节输出流)
  • Reader(字符输入流)
  • Writer(字符输出流)
    所有IO流对象都直接或者间接继承自这四个基本抽象类

重要的IO流

四个节点流

FileInputStream

首先准备一个文本文件info.txt,里边的内容你随便写

我们尝试读取该文件,并将内容打印到控制台上

第一种方法

 public static void testFileInputStream1() {
            FileInputStream fis = null;
            try {
                //1.创建实体文件的File对象
                File file = new File("info.txt");
                //2.创建FileInputStream对象
                fis = new FileInputStream(file);
                int b = 0;
    
                //3.调用read方法,不断从io流中读取数据
                /** * 此处的while充分体现了“流”的概念,表示源源不断从IO中进行读取 * 注意read()方法是一个字节一个字节地进行读取。是效率最低的一种方法,不推荐使用 * * 由于文本内容都是ascii字符,因此将读取的字节b进行(char)强转可以正常打印 * 但是如果超出char的表示范围(如汉字),不能采用该种方式,否则会出现乱码的情况 */
                while ((b = fis.read()) != -1) {
                    System.out.print((char) (b));
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (fis != null) {
                    try {
                        fis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

使用read()

 public static void testFileInputStream2() {
            FileInputStream fis = null;
            try {
                //1.创建实体文件的File对象
                File file = new File("info.txt");
                //2.创建FileInputStream对象
                fis = new FileInputStream(file);
                int len = 0;
                byte[] bytes = new byte[20];
                String str;
    
                //3.调用read方法,不断从io流中读取数据
                /** * read(byte[] buf)函数同时从流中读取若干个字节,返回值为该次读取的字节个数,如果到达文件末尾则返回-1 */
                while ((len = fis.read(bytes)) != -1) {
                    //采用如下的打印方式可以避免乱码问题
                    str = new String(bytes, 0, len);
                    System.out.print(str);
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (fis != null) {
                    try {
                        fis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

FileOutputStream

我们使用FileOutputStream和前文的FileInputStream实现文件拷贝

 public static void testFileOutputStream() {
            File inputFIle = new File("input.txt");
            File outputFIle = new File("output.txt");
    
            FileInputStream fis = null;
            FileOutputStream fos = null;
    
            try {
                fis = new FileInputStream(inputFIle);
                fos = new FileOutputStream(outputFIle);
                int len = 0;
                byte[] bytes = new byte[1024];
                while ((len = fis.read(bytes)) != -1) {
                    fos.write(bytes, 0, len);
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                //先关闭输出流
                if (fos != null) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
    
                if (fis != null) {
                    try {
                        fis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
    
        }

FileReader/FileWriter

同样我们使用字节节点流来实现文件拷贝

 /** * 利用FileReader/FileWriter实现文件复制 */
    public static void testFileReaderAndFileWriter() {
        File inputFile = new File("input.txt");
        File outputFile = new File("output.txt");
    
        FileReader fr = null;
        FileWriter fw = null;
        try {
            fr = new FileReader(inputFile);
            fw = new FileWriter(outputFile);
    
            int len = 0;
            //API和FileInputStream/FileOutputStream基本相同
            //不同点在于处理的基本单位不同,本例中使用的是字符数组
            char[] chars = new char[20];
            while ((len = fr.read(chars)) != -1) {
                fw.write(chars, 0, len);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fw != null) {
                try {
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fr != null) {
                try {
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


处理流之四种缓冲流

  • BufferedInputStream
  • BufferedOutputStream
  • BufferedReader
  • BufferedWriter

BufferdInputStream/BufferedOutputStream

首先我们看BufferdInputStream/BufferedOutputStream,他是对InputStream的实现类的包装流,即它的构造函数需要传递一个InputStream的实现类,具体用法高度套路化,还是以文件拷贝为例,下面看具体代码。

 public static void testBufferedStream() {
    
            FileInputStream fileInputStream = null;
            FileOutputStream fileOutputStream = null;
            try {
                //1.万年不变的,创建File对象
                File inputFile = new File("input.txt");
                File outputFile = new File("output.txt");
    
                //2.万年不变的,包装一层FileInputStream/FilePutputStream
                fileInputStream = new FileInputStream(inputFile);
                fileOutputStream = new FileOutputStream(outputFile);
    
                //重点来了!创建缓冲流对象,将fileInputStream/fileOutputStream作为参数传递进去
                //3.高度套路化的缓冲流的创建
                BufferedInputStream bis = new BufferedInputStream(fileInputStream);
                BufferedOutputStream bos = new BufferedOutputStream(fileOutputStream);
    
                //4.利用缓冲流进行读取,基本的API和之前的没啥区别
                int len = 0;
                byte[] bytes = new byte[1024];
                while ((len = bis.read()) != -1) {
                    bos.write(bytes, 0, len);
                    //5.这是使用缓冲流需要注意的一个点,write()之后寄的flush()一下
                    bos.flush();
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                //.....略
            }
        }

注:有了缓冲流之后我们一般不会再直接使用节点流来操作数据,使用缓冲流会极大地提高效率。此外,如果是纯文本文件的读取,尽量使用字符流,效率比字节流要高。

FileReader/FileWriter

最后介绍一下BufferedReaderBufferedWriter,从名字上就可以看出来了,和FileReader/FileWriter又是配套的一组,代码其实和上面一样的套路。唯一不同的一点,而且是比较重要的一点,便是BufferedReader多了一个readLine()的API,便于我们直接读取一行。

代码还是写一遍吧…都是高度套路

 public static void testBufferedReaderAndWriter() {
    
            FileReader fileReader = null;
            FileWriter fileWriter = null;
            try {
                //1.万年不变的,创建File对象
                File inputFile = new File("input.txt");
                File outputFile = new File("output.txt");
    
                //2.万年不变的,包装一层FileReader/FileWriter
                fileReader = new FileReader(inputFile);
                fileWriter = new FileWriter(outputFile);
    
                //重点来了!创建缓冲流对象,将fileReader/fileWriter作为参数传递进去
                //3.高度套路化的缓冲流的创建
                BufferedReader br = new BufferedReader(fileReader);
                BufferedWriter bw = new BufferedWriter(fileWriter);
    
                //4.利用缓冲流进行读取,除了多一个readLine()之外,API区别不大
                //用于接收读取一行的返回结果,不要不停判断是否为null,而不是之前的-1
                String str = "";
                while ((str = br.readLine()) != null) {
                    bw.write(str);
                    //换行操作,否则拷贝之后的文件都是一行
                    bw.newLine();
                    //5.这是使用缓冲流需要注意的一个点,write()之后寄的flush()一下
                    bw.flush();
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                //.....略
            }
        }

处理流之两个转换流

InputStreamReader/OutputStreamWriter

这两种转换流比较有玩儿,InputStreamReader是将字节流转换为字符流。

有的人可能会问,你一开始使用字符流不就完了?干嘛多此一举还得转换一次?原因有二:一是有人就闲得慌,先用字节流处理…毕竟因为字节流是万能的嘛。其二,我们可以在字节流转字符流的过程中设置自己想要的编码格式,获得我们想要的结果,这也就是所谓的解码过程。

举个例子来说,二进制流0xE68891 (这里表示为16进制,本质是一样的),如果用utf8 字符集来解码,得到的结果为’我’ ;如果用iso-8859-1,也就是latin1字符集去解释这串字节 ,解释后的字符串就是'我' 。各位看到这里或许就明白了吧。

接下来的理解就简单了,OutputStreamWriter是将字符输出流转换为字节输出流。一图胜千言,上图!

demo如下,看一下注释大家就会很明白

	/** * 测试转换流 */
    public static void testStreamReaderWriter() {
        File inputFile = new File("input.txt");
        File outputFile = new File("outputFile.txt");

        /** * 解码 */
        FileInputStream fis = null;
        InputStreamReader isr = null;

        /** * 编码 */
        FileOutputStream fos = null;
        OutputStreamWriter osw = null;

        try {
            fis = new FileInputStream(inputFile);
            //这里的第二个参数表示从字节到字符采用何种字符集来解码
            //如果字符集不足以包含你期望的所有字符,则会出现乱码
// isr = new InputStreamReader(fis,"ISO8859-1");//中文情况下乱码
            isr = new InputStreamReader(fis, "UTF8");//正常
            //isr已经是字符流,自然我们就可以加一层处理流,BufferedReader
            BufferedReader br = new BufferedReader(isr);


            fos = new FileOutputStream(outputFile);
            osw = new OutputStreamWriter(fos);
            //osw同样是字符流,继续采用处理流提高速度
            BufferedWriter bw = new BufferedWriter(osw);

            String str = "";
            while ((str = br.readLine()) != null) {
                bw.write(str);
                bw.newLine();
                bw.flush();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //异常处理
        }
    }
全部评论

相关推荐

秋招倒计时了,有没有一起组队捡漏的?
牛客965593684号:我倒是觉得捡漏机会不多,现在大厂都精得很,超发一大堆offer,还是等春招吧
点赞 评论 收藏
分享
10-05 23:02
东北大学 Java
我说句实话啊:那时候看三个月培训班视频,随便做个项目背点八股,都能说3 40w是侮辱价
点赞 评论 收藏
分享
09-27 10:54
重庆大学 C++
人已微死:致敬传奇耐测王。
投递小米集团等公司10个岗位
点赞 评论 收藏
分享
11-03 08:32
门头沟学院 Java
武汉启云方 Java 13k每月,公积金5%
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务