Java中常见IO流的介绍与使用

一、简介

  流是一种抽象概念,它代表了数据的无结构化传递。按照流的方式进行输入输出,数据被当成无结构的字节序或字符序列。从流中取得数据的操作称为提取操作,而向流中添加数据的操作称为插入操作。用来进行输入输出操作的流就称为IO流。换句话说,IO流就是以流的方式进行输入输出。

1.1 I/O流的分类

  • 根据处理数据类型的不同分为:字符流和字节流
  • 根据数据流向不同分为:输入流和输出流

1.2 I/O流抽象基类

  • 字节流的抽象基类:InputStream,OutputStream
  • 字符流的抽象基类:Reader,Writer

1.3 I/O流概览图

在这里插入图片描述

二、常见IO流的详细介绍与使用

2.1 FileInputStream & FileOutputStream(字节流)

  FileInputStreamFileOutputStream,分别是InputStream(字节输入流)OutputStream(字节输出流) 抽象基类的子类,Java中常常以字节流的形式操作文件内容。比如下例:我们操作一个图片。

    /**
     * FileInputStream & FileOutputStream (字节流)
     */
    @Test
    public void fileInputStreamAndFileOutputStream() {
        File sourceFile = new File("C:\\myFile\\example\\Base64编译过程.png");
        File destFile = new File("C:\\myFile\\example\\byteStream\\Base64编译过程(字节流结果).png");
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            System.out.println("FileInputStream & FileOutputStream 字节流开始文件读写");
            LocalDateTime startTime = LocalDateTime.now();//获取操作开始时间
            fis = new FileInputStream(sourceFile); //读取源文件的流
            fos = new FileOutputStream(destFile); //输出到目标文件的流
            System.out.println("当前文件输入流中的字节数为:" + fis.available());
            //可以根据文件的大小和内存的大小合理分配大小比如2048
            byte[] bytes = new byte[1024];
            int len;
            //读入多个字节到字节数组中,len为一次读入的字节数
            while ((len = fis.read(bytes)) != -1) {
                //将字节数组写入到输出流(偏移量从0开始,到len字节)
                fos.write(bytes, 0, len);
//                System.out.println("当前字节输入流中剩余字节数:" + fis.available());
            }
            LocalDateTime endTime = LocalDateTime.now();//获取操作结束时间
            long millis = ChronoUnit.MILLIS.between(startTime, endTime);
            System.out.println("FileInputStream & FileOutputStream 字节流文件读写完毕,耗时:" + millis + "毫秒");
        } catch (IOException e) {
            System.out.println("FileInputStream & FileOutputStream 字节流文件读写异常" + e);
        } finally {
            try {
                if (fos != null) {
                    fos.close();//关闭输出流
                }
                if (fis != null) {
                    fis.close();//关闭输入流
                }
            } catch (IOException e) {
                System.out.println("FileInputStream & FileOutputStream 字节流文件读写关闭异常" + e);
            }
        }
    }

注意:

  • FileInputStream要操作的文件必须存在,否则会抛出异常
  • FileOutputStream写入的目的文件不存在时会被创建文件,存在的时会被覆盖文件
  • FileOutputStream通过构造函数的第二个参数true或者false可以实现文件追加功能
  • FileInputStream读取字节的时,返回值len表示本次读取了多少个字节,通常情况下每次读取1024个字节,也根据实际文件大小及内存大小可以合理取值
  • FileInputStream读取到字节的末尾,再继续读取,会返回 -1,可以当做我们文件读取完毕的标识符
  • 字节流(不包含缓存字节流)是不存在缓冲区的,字节的读取和写入都是通过操作系统来实现的,所以不需要使用flush操作刷新缓冲区
  • 流都要进行关闭操作,还需要 try…catch,一般放到finally里

运行结果:

	FileInputStream & FileOutputStream 字节流开始文件读写
	当前文件输入流中的字节数为:136363
	FileInputStream & FileOutputStream 字节流文件读写完毕,耗时:2毫秒

2.2 InputStreamReader & OutputStreamWriter(字符流-又称转换流)-不推荐使用

  InputStreamReader OutputStreamWriter,分别是Reader(字符输入流)Writer(字符输出流) 抽象基类的子类,Java中常常以字符流的形式操作文件内容,InputStreamReader和OutputStreamWriter都是转换流。

  • InputStreamReader 是字节流通向字符流的桥梁,它将字节流转换为字符流。
  • OutputStreamWriter是字符流通向字节流的桥梁,它将字符流转换为字节流。
	/**
     * InputStreamReader & OutputStreamWriter (字符流,不推荐使用,但是可以当转换流)
     */
    @Test
    public void inputStreamReaderAndOutputStreamWriter() {
        File sourceFile = new File("C:\\myFile\\example\\Alian的文章.txt");
        File destFile = new File("C:\\myFile\\example\\exchangeStream\\Alian的文章(转换流结果).txt");
        InputStreamReader isReader = null;
        OutputStreamWriter osWriter = null;
        try {
            System.out.println("InputStreamReader & OutputStreamWriter 转换流开始文件读写");
            LocalDateTime startTime = LocalDateTime.now();//获取操作开始时间
            FileInputStream fis = new FileInputStream(sourceFile);//读取源文件的流
            FileOutputStream fos = new FileOutputStream(destFile);//输出到目标文件的流
            isReader = new InputStreamReader(fis, StandardCharsets.UTF_8);//读取时编码
            osWriter = new OutputStreamWriter(fos, "gb2312");//输出时编码
            System.out.println("当前文件输入流中的字节数为:" + fis.available());
            char[] bytes = new char[1024];
            int len;
            //读入多个字符到字符数组中,len为一次读入的字符数
            while ((len = isReader.read(bytes)) != -1) {
                //将字符数组写入到输出流(偏移量从0开始,到len字节)
                osWriter.write(bytes, 0, len);
//                System.out.println(String.copyValueOf(bytes, 0, len));
            }
            //刷新流
            osWriter.flush();
            LocalDateTime endTime = LocalDateTime.now();//获取操作结束时间
            long millis = ChronoUnit.MILLIS.between(startTime, endTime);
            System.out.println("InputStreamReader & OutputStreamWriter 转换流文件读写完毕,耗时:" + millis + "毫秒");
        } catch (IOException e) {
            System.out.println("InputStreamReader & OutputStreamWriter 转换流文件读写异常" + e);
        } finally {
            try {
                if (osWriter != null) {
                    osWriter.close();//关闭输出流(会先调用flush方法)
                }
                if (isReader != null) {
                    isReader.close();//关闭输入流
                }
            } catch (IOException e) {
                System.out.println("InputStreamReader & OutputStreamWriter 转换流文件读写关闭异常" + e);
            }
        }
    }

注意:

  • InputStreamReader和OutputStreamWriter内部都存在缓冲区
  • InputStreamReader构造函数需要传递字节流和编码格式,没有传编码格式则会使用系统默认编码,容易导致乱码(windows默认编码表为GBK)
  • OutputStreamWriter如果是追加文件内容,需要保证原文件之前编码和构造函数传入的编码格式一致,否则当有中文时会出现乱码
  • 传递给字符流的字节流不需要单独的进行关闭,在字符流关闭的时候会自动调用字节流的close()方法

运行结果:

InputStreamReader & OutputStreamWriter 转换流开始文件读写
当前文件输入流中的字节数为:454
InputStreamReader & OutputStreamWriter 转换流文件读写完毕,耗时:2毫秒

打开文件,我们可以看到文件的编码变成了gb2312

2.3 FileReader & FileWriter(字符流)

  FileReader FileWriter,分别是InputStreamReader (字节流转换为字符流)OutputStreamWriter(字符流转换为字节流) 抽象基类的子类,Java中常常以字符流的形式操作文件内容。

    /**
     * FileReader & FileWriter (字符流)
     */
    @Test
    public void fileReaderAndFileWriter() {
        File sourceFile = new File("C:\\myFile\\example\\Alian的文章.txt");
        File destFile = new File("C:\\myFile\\example\\charStream\\Alian的文章(字符流结果).txt");
        FileReader fReader = null;
        FileWriter fWriter = null;
        try {
            System.out.println("---------------------FileReader & FileWriter 字符流开始文件读写---------------------");
            LocalDateTime startTime = LocalDateTime.now();//获取操作开始时间
            fReader = new FileReader(sourceFile);//读取源文件的流
            fWriter = new FileWriter(destFile);//输出到目标文件的流
            char[] bytes = new char[1024];
            int len;
            //读入多个字符到字符数组中,len为一次读入的字符数
            while ((len = fReader.read(bytes)) != -1) {
                //将字符数组写入到输出流(偏移量从0开始,到len字节)
                fWriter.write(bytes, 0, len);
                System.out.println(String.copyValueOf(bytes, 0, len));
            }
            //刷新流
            fWriter.flush();
            LocalDateTime endTime = LocalDateTime.now();//获取操作结束时间
            long millis = ChronoUnit.MILLIS.between(startTime, endTime);
            System.out.println("---------------------FileReader & FileWriter 字符流文件读写完毕,耗时:" + millis + "毫秒---------------------");
        } catch (IOException e) {
            System.out.println("FileReader & FileWriter 字符流文件读写异常" + e);
        } finally {
            try {
                if (fWriter != null) {
                    fWriter.close();//关闭输出流(会先调用flush方法)
                }
                if (fReader != null) {
                    fReader.close();//关闭输入流
                }
            } catch (IOException e) {
                System.out.println("FileReader & FileWriter 字符流文件读写关闭异常" + e);
            }
        }
    }

注意:

  • FileReader和FileWriter内部都存在缓冲区,默认大小为8192字节
  • 没有进行流的关闭操作,数据会存在缓冲区中,不会存储到文件上,只有当数据超过了缓冲区的大小,或者手动调用flush方法,数据才会刷新到文件上
  • 调用close方法的内部会先调用flush刷新缓冲区

运行结果:

---------------------FileReader & FileWriter 字符流开始文件读写---------------------
Java实现MD5工具类
Java实现SHA-1、SHA-256和SHA-512加密(原生摘要)
Java实现Base64工具类(编码和解码)
Java实现3DES工具类(包含CBC和ECB)
Java实现AES工具类(包含CBC和ECB)
Java实现RSA工具类(加密、解密、签名、验签)
Java8新特性:Lambda表达式详解及四大函数式接口
Java8新特性:Stream详细使用
Java8新特性:LocalDateTime详细介绍
Java文件基础操作
---------------------FileReader & FileWriter 字符流文件读写完毕,耗时:0毫秒---------------------

2.4 BufferedInputStream & BufferedOutputStream(缓存字节流)-推荐使用

  BufferedInputStream BufferedOutputStream,分别是FileInputStream(文件输入流)FileOutputStream(文件输出流) 的子类,用法上也差不多,只不过BufferedInputStream BufferedOutputStream提供了一个合适的缓冲区来提高读写性能,在读写大数据的时候效果明显。下例是读取20万行大数据文本文件的测试,不到100毫秒就完成读写操作。

	/**
     * BufferedInputStream & BufferedOutputStream(缓存字节流)(推荐使用)
     */
    @Test
    public void bufferedInputStreamAndBufferedOutputStream() {
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            System.out.println("BufferedInputStream & BufferedOutputStream 缓存字节流开始文件读写");
            FileInputStream inputStream = new FileInputStream("C:\\myFile\\example\\20万行大数据文本.txt");
            FileOutputStream outputStream = new FileOutputStream("C:\\myFile\\example\\cacheByteStream\\20万行大数据文本(缓存字节流).txt");
            LocalDateTime startTime = LocalDateTime.now();//获取操作开始时间
//            bis = new BufferedInputStream(inputStream);//读取源文件的流
//            bos = new BufferedOutputStream(outputStream);//输出到目标文件的流
            bis = new BufferedInputStream(inputStream,20*1024);//读取源文件的流,默认是8192
            bos = new BufferedOutputStream(outputStream,20*1024);//输出到目标文件的流,默认是8192
            System.out.println("当前缓存字节流中的字节数:" + bis.available());
            byte[] bytes = new byte[1024];
            int len;
            //读入多个字节到字节数组中,len为一次读入的字节数
            while ((len = bis.read(bytes)) != -1) {
                //将字节数组写入到输出流(偏移量从0开始,到len字节)
                bos.write(bytes, 0, len);
            }
            bos.flush();
            LocalDateTime endTime = LocalDateTime.now();//获取操作结束时间
            long millis = ChronoUnit.MILLIS.between(startTime, endTime);
            System.out.println("BufferedInputStream & BufferedOutputStream 缓存字节流文件读写完毕,耗时:" + millis + "毫秒");
        } catch (IOException e) {
            System.out.println("BufferedInputStream & BufferedOutputStream 缓存字节流文件读写异常" + e);
        } finally {
            try {
                if (bos != null) {
                    bos.close();//关闭输出流(会先调用flush方法)
                }
                if (bis != null) {
                    bis.close();//关闭输入流
                }
            } catch (IOException e) {
                System.out.println("BufferedInputStream & BufferedOutputStream 缓存字节流文件读写关闭异常" + e);
            }
        }
    }

注意:

  • BufferedInputStream和BufferedOutputStream内部都存在缓冲区,默认大小为8192字节

运行结果:

BufferedInputStream & BufferedOutputStream 缓存字节流开始文件读写
当前缓存字节流中的字节数:56626711
BufferedInputStream & BufferedOutputStream 缓存字节流文件读写完毕,耗时:54毫秒

2.5 BufferedReader & BufferedWriter(缓存字符流)-推荐使用

  BufferedReaderBufferedWriter,分别是Reader(字符输入流)Writer(字符输出流) 抽象基类的子类,常用于字符流形式的文件读写。下例是读取45万行大数据文本文件的测试,不到1秒就读写完毕了。

	/**
     * BufferedReader & BufferedWriter(缓存字符流)(推荐使用)
     */
    @Test
    public void bufferedReaderAndBufferedWriter() {
        File sourceFile = new File("C:\\myFile\\example\\45万行大数据文本.txt");
        File destFile = new File("C:\\myFile\\example\\cacheCharStream\\45万行大数据文本(缓存字符流).txt");
        BufferedReader bufReader = null;
        BufferedWriter bufWriter = null;
        try {
            System.out.println("BufferedReader & BufferedWriter 缓存字符流开始文件读写");
            LocalDateTime startTime = LocalDateTime.now();//获取操作开始时间
            FileReader reader = new FileReader(sourceFile);
            FileWriter writer = new FileWriter(destFile);
            bufReader = new BufferedReader(reader,20*1024);//读取源文件的流,默认是8192字节
            bufWriter = new BufferedWriter(writer,20*1024);//输出到目标文件的流,默认是8192字节
            String line;
            //读入一行数据
            while ((line = bufReader.readLine()) != null) {
                //写入一行数据
                bufWriter.write(line);
                bufWriter.newLine();//换行(文件最后一个换行无需处理)
            }
            LocalDateTime endTime = LocalDateTime.now();//获取操作结束时间
            long millis = ChronoUnit.MILLIS.between(startTime, endTime);
            System.out.println("BufferedReader & BufferedWriter 缓存字符流文件读写完毕,耗时:" + millis + "毫秒");
        } catch (IOException e) {
            System.out.println("BufferedReader & BufferedWriter 缓存字符流文件读写异常" + e);
        } finally {
            try {
                if (bufWriter != null) {
                    bufWriter.close();//关闭输出流
                }
                if (bufReader != null) {
                    bufReader.close();//关闭输入流
                }
            } catch (IOException e) {
                System.out.println("BufferedInputStream & BufferedOutputStream 缓存字符流文件读写关闭异常" + e);
            }
        }
    }

注意:

  • BufferedReader和BufferedWriter内部都存在缓冲区,默认大小为8192字节
  • 提供的方法ReadLine(),根据合适的换行符来读取一行数据
  • 提供的方法ReadAllBytes(),根据合适的换行符来读取所有字节
  • 提供的方法newLine(),会根据操作系统的不同添加合适的换行符
  • 按行来读取文件时,如果下一行没有内容,继续读取下一行,则会返回 null,可以当做我们文件读取完毕的标识符

运行结果:

BufferedReader & BufferedWriter 缓存字符流开始文件读写
BufferedReader & BufferedWriter 缓存字符流文件读写完毕,耗时:681毫秒

结语

  以上就是今天要讲的内容,本文仅仅简单介绍了Java中常见的IO流及其使用,后续有时间会继续扩充。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值