Java文件流

分类字节输入流字节输出流字符输入流字符输出流
抽象基类InputStreamOutputStreamReaderWriter
文件流FileInputStreamFileOutputStreamFileReaderFileWriter
数组流ByteArrayInputStreamByteArrayOutputStreamCharArrayReaderCharArrayWriter
管道流PipedInputStreamPipedOutputStreamPipedReaderPipedWriter
字符串流StringReaderStringWriter
缓冲流BufferedInputStreamBufferedOutputStreamBufferedReaderBufferedWriter
转换流InputStreamReaderOutputStreamWriter
对象流ObjectInputStreamObjectOutputStream
过滤流(超类)FilterInputStreamFilterOutputStreamFilterReaderFilterWriter
打印流PrintStreamPrintWriter
PushbackInputStreamPushbackReader
DataInputStreamDataOutputStream

后端实际业务中对文件操作无非两种情况。

1. 不关心文件内容,直接对文件进行迁移传输操作

2. 需要具体解析文件数据。

流的分类

流按照读写单位划分

  • 字节流:按照字节方式读写,一次读一个字节,可以读写所有类型文件。一般结尾带有Stream的类都是字节流。
  • 字符流:按照字符方式读写,一次读一个字符,只能读文本类型(音频、图片、视频不可读)。一般结尾带有Reader、Writer的类都是字符流。

流按照流的方向划分

  • 输入流:外部文件流入到程序中就是输入流。一般带有Input、Reader的类都是输入流。
  • 输出流:程序内部流出到外部就是输出流。一般带有Output、Writer的类都是输出流

简单分析

以下示例只是demo,没有那么全面,仅供参考

FileInputStream、FileOutputStream类 

FileInputStream

        //文件路径,相对/绝对路径都可
        String filePath = "a.zip";
        File file = new File(filePath);
        //以下两种构造方法
        InputStream is1 = new FileInputStream(file);
        InputStream is2 = new FileInputStream(filePath);
        InputStream is = new FileInputStream("A.txt");
        //两种读取方式

        int temp = 0;
        //一次读一个字节,返回值temp代表读取字节的ASCII码值,若为-1表示读取完毕;不推荐使用,读取效率太慢了
        while ((temp = is.read()) != -1) {
            System.out.println((char) temp);
        }
        /**
         * 代码中,上面已经读取完毕后,下面代码无法重新读取
         * FileInputStream无法改变指针的位置,若是借助其它缓冲流到可以做到
         */
        //一次最多读取1024个字节大小进入缓冲区,缓冲区大小可以自定义,会影响效率
        byte[] buffer = new byte[1024];
        int len = 0;
        //len代表当前读取的字节长度
        while ((len = is.read(buffer)) != -1) {
            String str = new String(buffer, 0, len);
            System.out.println(str);
        }
        is.close();

FileOutputStream

        //文件路径,相对/绝对路径都可
        String filePath = "safe/test/file.txt";
        File file = new File(filePath);

        /**
         * 文件不存在,父目录存在时,FileOutputStream会自动创建文件
         * 如该文件的父级目录也不存在则无法创建,会报异常
         */
        File parentFile = file.getParentFile();
        if(!parentFile.exists()) {
            //创建多级目录
            parentFile.mkdirs();
        }
        //多种构造方法
        //默认直接覆盖原始文件
        OutputStream os1 = new FileOutputStream(file);
        OutputStream os2 = new FileOutputStream(filePath);
        //在原文件基础上追加内容
        OutputStream os3 = new FileOutputStream(file, true);
        OutputStream os4 = new FileOutputStream(filePath, true);
        OutputStream os = new FileOutputStream("file.txt");
        //写入数据的三种方式
        //字节数组写入流中
        os.write("Hello World !!!".getBytes());
        //一个字节的ASCII码值写入流中;不推荐使用,写入效率太慢了
        os.write(97);
        //从字节数组的下标1开始,写入3位至流中
        byte[] bytes = "Hello World".getBytes();
        os.write(bytes, 1, 3);
        //强制将缓冲区的数据写入到文件中
        os.flush();
        //关闭流
        os.close();

联合使用

        //计时器
        TimeInterval timer = DateUtil.timer();
        InputStream is = new FileInputStream("a.zip");
        OutputStream os = new FileOutputStream("b.zip");
        
        /**
         * 35MB大小文件进行测试
         * 缓冲区为1kb,花费时间大约为250毫秒
         * 缓冲区为8kb,花费时间大约为50毫秒
         * 缓冲区为100kb,花费时间大约为25毫秒
         * 缓冲区为1Mb,花费时间大约为45毫秒
         * 由此可见,缓冲区并不是越大越好
         */
        int len = 0;
        byte[] buffer = new byte[1024 * 100];
        while ((len = is.read(buffer)) != -1) {
            os.write(buffer, 0, len);
        }
        os.flush();
        os.close();
        is.close();
        //打印耗时
        System.out.println("读写花费时间:" + timer.interval() + "毫秒");

FileReader、FileWriter类

字符流和字节流操作方法类似,这里不多做解释

FileReader

        //文件路径,相对/绝对路径都可
        String filePath = "A.txt";
        File file = new File(filePath);
        Reader reader1 = new FileReader(file);
        Reader reader2 = new FileReader(filePath);
        Reader reader = new FileReader("A.txt");
        int temp = 0;
        while ((temp = reader.read()) != -1) {
            System.out.println((char) temp);
        }

        int len = 0;
        char[] buffer = new char[1024];
        while ((len = reader.read(buffer)) != -1) {
            String str = new String(buffer, 0, len);
            System.out.println(str);
        }
        reader.close();

FileWriter

        String filePath = "file.txt";
        File file = new File(filePath);
        Writer writer1 = new FileWriter(file);
        Writer writer2 = new FileWriter(filePath);
        Writer writer3 = new FileWriter(file, true);
        Writer writer4 = new FileWriter(filePath, true);
        Writer writer = new FileWriter("file.txt");
        //写入一个字符
        writer.write('a');
        //写入一个字符串
        writer.write("abc");
        //写入一个字符数组
        writer.write("Hello".toCharArray());
        //从下标1开始,长度为3,截取字符串写入
        writer.write("hello world", 1, 3);
        //和writer.write('a')一致
        writer.append('a');
        //和writer.write("abc")一致
        writer.append("Hello");
        //从下标1开始,长度为3,截取字符串写入
        writer.append("Hello", 1, 3);
        writer.flush();
        writer.close();

联合使用

        Reader reader = new FileReader("A.txt");
        Writer writer = new FileWriter("B.txt");
        int len = 0;
        char[] buffer = new char[1024];
        while ((len = reader.read(buffer)) != -1) {
            writer.write(buffer, 0, len);
        }
        writer.flush();
        writer.close();
        reader.close();

ByteArrayInputStream、ByteArrayOutputStream类

简单来说看作是一个字节数组对象,起到缓冲作用

ByteArrayInputStream

       /**
         * 字节数组缓冲区,可重复读
         */
        String str = "Hello world!!!";
        ByteArrayInputStream bis = new ByteArrayInputStream(str.getBytes());
        int temp = 0;
        while ((temp = bis.read()) != -1) {
            System.out.println((char) temp);
        }
        //参数无意义,主要是标记当前读取的位置,一般用不到。
        //bis.mark(0);
        /**
         * 将光标移至标记位
         * 此处就可以连续读两次
         */
        bis.reset();
        int len = 0;
        byte[] buffer = new byte[1024];
        while ((len = bis.read(buffer)) != -1) {
            String result = new String(buffer, 0, len);
            System.out.println(result);
        }

ByteArrayOutputStream

        OutputStream os = new FileOutputStream("file.txt");
        /**
         * 当你导出一个excel文件或者压缩文件时,前端需要你在响应头中返回文件大小应该怎么做
         * 就可以用到ByteArrayOutputStream作为临时缓冲区,可以计算大小
         */
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        bos.write("Hello world!!!".getBytes());
        System.out.println(bos.size());
        //将字节数组缓冲区写入输出流中
        bos.writeTo(os);
        os.close();

 PipedInputStream、PipedOutputStream类

它们的作用是让多线程可以通过管道流实现线程间的通信。使用管道通信时,两者必须配套使用。

大致流程是:线程A向PipedOutputStream中写入数据,数据会自动发送至配套的PipedInputStream中,线程B就可以读取PipedInputStream中的数据,这样就实现了线程间的通信

public class Sender implements Runnable {

    private PipedOutputStream os = new PipedOutputStream();

    public PipedOutputStream getOutputStream() {
        return os;
    }

    @Override
    public void run() {
        String str = "我给你发消息了";
        byte[] bytes = str.getBytes();
        try {
            //写入数据
            os.write(bytes, 0, bytes.length);
            os.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
public class Receiver implements Runnable {
    private PipedInputStream is = new PipedInputStream();

    public PipedInputStream getInputStream() {
        return is;
    }

    @Override
    public void run() {
        // 读取数据的方式和FileInputStream一致
        byte[] buffer = new byte[1024];
        int len = 0;
        try {
            if ((len = is.read(buffer)) != -1) {
                String str = new String(buffer, 0, len);
                System.out.println(str);
            }
            is.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
    public void test1() {
        //创建对象,不能写成Runnable sender = new Sender(),因为上转型对象不能调用子类独有方法
        Sender sender = new Sender();
        Receiver receiver = new Receiver();
        PipedOutputStream os = sender.getOutputStream();
        PipedInputStream is = receiver.getInputStream();
        try {
            //将输出和输入管道连接起来
            os.connect(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //启动线程
        new Thread(sender).start();
        try {
            //睡一秒,确保先写后读
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(receiver).start();
    }

BufferedInputStream、BufferedOutputStream类

BufferedInputStream

        TimeInterval timer = DateUtil.timer();
        InputStream is = new FileInputStream("a.zip");
        /**
         * 高级流,和低级流配合使用
         * 缓冲区的作用,提高读取效率,可重复读
         * 35MB文件
         * 不使用缓冲流,一次读一个字节,花费25秒左右
         * 使用缓冲流,一次读一个字节,花费700毫秒左右。可能是一次读一个字节原因,改变缓冲区大小并不会提升效率
         * 缓冲区大小默认是8kb,可以自定义设置
         */
        BufferedInputStream bis = new BufferedInputStream(is, 1024 * 100);
        int temp = 0;
        while ((temp = bis.read()) != -1) {
            System.out.println((char) temp);
        }
        bis.close();
        System.out.println(timer.interval());
        TimeInterval timer = DateUtil.timer();
        InputStream is = new FileInputStream("a.zip");
        /**
         * 高级流,和低级流配合使用
         * 缓冲区的作用,提高读取效率
         * 35MB文件,可能文件太小,最低10毫秒左右,差距不明显
         * 不使用缓冲流;一次最多读字节数组1kb,花费时间40毫秒左右
         * 不使用缓冲流;一次最多读字节数组100kb,花费时间10毫秒左右
         * 使用缓冲流,默认大小8kb;一次最多读字节数组1kb,花费时间15毫秒左右
         * 使用缓冲流,默认大小8kb;一次最多读字节数组100kb,花费时间10毫秒左右
         * 使用缓冲流,100kb;一次最多读字节数组1kb,花费时间10毫秒左右
         * 使用缓冲流,100kb;一次最多读字节数组100kb,花费时间10毫秒左右
         * 缓冲区大小默认是8kb,可以自定义设置
         */
        BufferedInputStream bis = new BufferedInputStream(is, 1024 * 100);
        int len = 0;
        byte[] buffer = new byte[1024 * 100];
        while ((len = bis.read(buffer)) != -1) {
            String str = new String(buffer, 0, len);
            System.out.println(str);
        }
        System.out.println(timer.interval());

联合使用测试效率问题

       /**
         * 35MB文件,文件有点小,效率达到阈值,没法有很大差别
         *  输入输出都不用缓冲流,一次最多读取字节1kb,花费时间250毫秒左右
         *  输入输出都不用缓冲流,一次最多读取字节100kb,花费时间25毫秒左右
         *  输入用缓冲流默认8kb,输出不用缓冲流,一次最多读取字节1kb,花费时间230毫秒左右
         *  输入用缓冲流默认8kb,输出不用缓冲流,一次最多读取字节100kb,花费时间25毫秒左右
         *  输入用缓冲流100kb,输出不用缓冲流,一次最多读取字节1kb,花费时间230毫秒左右
         *  输入用缓冲流100kb,输出不用缓冲流,一次最多读取字节100kb,花费时间25毫秒左右
         *  输入不用缓冲流,输出用缓冲流默认8kb,一次最多读取字节1kb,花费时间90毫秒左右
         *  输入不用缓冲流,输出用缓冲流默认8kb,一次最多读取字节100kb,花费时间25毫秒左右
         *  输入不用缓冲流,输出用缓冲流100kb,一次最多读取字节1kb,花费时间60毫秒左右
         *  输入不用缓冲流,输出用缓冲流100kb,一次最多读取字节100kb,花费时间25毫秒左右
         *  输入缓冲流8kb,输出缓冲流8kb,一次最多读取字节1kb,花费时间60毫秒左右
         *  输入缓冲流8kb,输出缓冲流8kb,一次最多读取字节100kb,花费时间25毫秒左右
         *  输入缓冲流100kb,输出缓冲流100kb,一次最多读取字节1kb,花费时间25毫秒左右
         *  输入缓冲流100kb,输出缓冲流100kb,一次最多读取字节100kb,花费时间25毫秒左右
         *
         *  以上可见,无论用不用缓冲流,一次最多读取字节大小对效率有很大影响。也不是越大越好
         */
        TimeInterval timer = DateUtil.timer();
        InputStream is = new FileInputStream("a.zip");
        BufferedInputStream bis = new BufferedInputStream(is, 1024 * 100);
        OutputStream os = new FileOutputStream("b.zip");
        BufferedOutputStream bos = new BufferedOutputStream(os, 1024 * 100);
        int len = 0;
        byte[] buffer = new byte[1024 * 100];
        while ((len = bis.read(buffer)) != -1) {
            bos.write(buffer, 0, len);
        }
        bos.close();
        os.close();
        bis.close();
        System.out.println(timer.interval());

InputStreamReader类

       /**
         * 将字节流转成字符流
         */
        InputStream is = new FileInputStream("A.txt");
        InputStreamReader reader = new InputStreamReader(is);
        int temp = 0;
        while ((temp = reader.read()) != -1) {
            System.out.println((char) temp);
        }
        reader.close();
        is.close();

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值