I/O高级流

经过我们之前的学习,我们对IO流的一些基本流如字节流和字符流都学习完毕了。

而在IO流的整个大体系中,他除了一些基本流的操作,他还有一些高级流等待着我们来解锁。

所以话不多说,今天我们先来学习其中一种高级流——缓冲流。

缓冲流

缓冲流,也叫高效流,是对输出和输入流的增强。

缓冲流的分类

缓冲流的基本原理

在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。

字节缓冲流

1. 构造方法

  • public BufferedInputStream(InputStream in):创建一个 新的缓冲输入流,需要传入一个InputStream类型参数。
  • public BufferedOutputStream(OutputStream out): 创建一个新的缓冲输出流,需要传入一个OutputStream 类型参数。

    演示如下:

    public class BufferedTest {
        public static void main(String[] args) throws FileNotFoundException {
            //构造方式一: 创建字节缓冲输入流【但是开发中一般常用下面的格式申明】
            FileInputStream fps = new FileInputStream("e:\\demo\\b.txt");
            BufferedInputStream bis = new BufferedInputStream(fps);
    
            //构造方式一: 创建字节缓冲输入流
            BufferedInputStream bis2 = new BufferedInputStream(new FileInputStream("e:\\demo\\b.txt"));
    
            //构造方式二: 创建字节缓冲输出流
            FileOutputStream fos = new FileOutputStream("e:\\demo\\b.txt");
            BufferedOutputStream bos = new BufferedOutputStream(fos);
        
            //构造方式二: 创建字节缓冲输出流
            BufferedOutputStream bos2 = new BufferedOutputStream(new FileOutputStream("e:\\demo\\b.txt"));
        }
    }
    复制代码

2. 感受高效

既然称缓冲流为高效流,那我们不得测试下看看,是骡子是马得拉出来溜溜啊。

先来感受下:

public class BufferedTest2 {
    public static void main(String[] args) {
        //使用多线程的方式来同时测试两者复制的速度。
        new Thread(() -> oldCopy()).start();//这个是JDK8新特性中的Lambda表达式
        new Thread(() -> bufferedCopy()).start();
        
        /*
           原样相当于:
           new Thread(new Runnable() {
                @Override
                public void run() {
                    oldCopy();
                }
            }).start();
         */
    }

    public static void oldCopy() {
        // 记录开始时间
        long start = System.currentTimeMillis();
        // 创建流对象
        try (
                FileInputStream fis = new FileInputStream("e:\\demo\\科比演讲.mp4");
                FileOutputStream fos = new FileOutputStream("e:\\demoCopy\\oldCopy.mp4")
        ){
            // 读写数据
            int b;
            while ((b = fis.read()) != -1) {
                fos.write(b);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 记录结束时间
        long end = System.currentTimeMillis();
        System.out.println("普通流复制时间:"+(end - start)+" 毫秒");
    }

    public static void bufferedCopy() {
        // 记录开始时间
        long start = System.currentTimeMillis();
        // 创建流对象
        try (
                BufferedInputStream bis = new BufferedInputStream(new FileInputStream("e:\\demo\\科比演讲.mp4"));
                BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("e:\\demoCopy\\newCopy.mp4"));
        ){
            // 读写数据
            int b;
            while ((b = bis.read()) != -1) {
                bos.write(b);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 记录结束时间
        long end = System.currentTimeMillis();
        System.out.println("缓冲流复制时间:"+(end - start)+" 毫秒");
    }
}

运行结果:
缓冲流复制时间:3498 毫秒
普通流复制时间:319839 毫秒 
复制代码

程序运行后,可以深刻的体验到缓冲流的快速,缓冲流已经复制成功了,但是普通流还没成功。不止没成功,而且耗时超级久。可以感受到缓冲流是普通流的几十倍了吧。我这个视频仅仅是46.2M大小而已,如果是更大的文件,几百M更甚至是几G的文件呢?那我岂不是一天都没办法传输完成了。这岂不是很百度网盘?

是不是觉得还不够快,想体验飞一般的感觉。现在我带你冲上云顶。嘿嘿,老司机要开车了 ,不要晕车哦。

接下来使用数组的方式进行体验:

public class BufferedTest3 {
    public static void main(String[] args) {
    //使用多线程的方式来同时测试两者复制的速度。
        new Thread(() -> oldCopy()).start();
        new Thread(() -> bufferedCopy()).start();

    }

    public static void oldCopy() {
        //开始时间
        long start = System.currentTimeMillis();

        try(
                FileInputStream fis = new FileInputStream("e:\\demo\\科比演讲.mp4");
                FileOutputStream fos = new FileOutputStream("e:\\demoCopy\\oldCopy2.mp4");
                ) {
            int len;
            byte[] b = new byte[1024];
            while((len = fis.read(b)) != -1) {
                fos.write(b, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        //结束时间
        long end = System.currentTimeMillis();
        System.out.println("普通流数组复制时间:" + (end - start) + "毫秒");
    }

    public static void bufferedCopy() {
        //开始时间
        long start = System.currentTimeMillis();
        // 创建流对象
        try (
                BufferedInputStream bis = new BufferedInputStream(new FileInputStream("e:\\demo\\科比演讲.mp4"));
                BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("e:\\demoCopy\\newCopy2.mp4"));
        ){
            // 读写数据
            int len;
            byte[] bytes = new byte[1024];
            while ((len = bis.read(bytes)) != -1) {
                bos.write(bytes, 0 , len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 结束时间
        long end = System.currentTimeMillis();
        System.out.println("缓冲流使用数组复制时间:"+(end - start)+" 毫秒");
    }
}

程序执行结果:
缓冲流使用数组复制时间:370 毫秒
普通流数组复制时间:1016毫秒
复制代码

是不是这个车速更快了,如果还不够快,你可以把 byte[] bytes = new byte[1024]改为 byte[] bytes = new byte[1024*8]。可以让你体验到更高效。你就会感觉这速度飞上天了。

字符缓冲流

1. 构造方法

  • public BufferedReader(Reader in) :创建一个 新的缓冲输入流,传入参数的类型为Reader

  • public BufferedWriter(Writer out): 创建一个新的缓冲输出流,传入参数的类型为Writer

    演示如下:

    // 创建字符缓冲输入流
    BufferedReader br = new BufferedReader(new FileReader("e:\\demo\\b.txt"));
    // 创建字符缓冲输出流
    BufferedWriter bw = new BufferedWriter(new FileWriter("e:\\demo\\b.txt"));
    复制代码

2. 特有方法

不用于字节缓冲流和字符普通流来说,他有别人所没有的东西,那是什么呢,我们现在来看看:

  • 字符缓冲输入流BufferedReaderpublic String readLine(): 读一行文字,如果读到最后,则会返回NULL

  • 字符缓冲输出流BufferedWriterpublic void newLine(): 写一行行分隔符即换行,由系统属性定义符号。

    一步一步来演示下:

    1. public void newLine()

      public class BufferedTest4 {
          public static void main(String[] args) throws IOException {
              BufferedWriter bw = new BufferedWriter(new FileWriter("e:\\demo\\hello.txt"));
      
              //写出数据
              bw.write("Hello");
              //换行
              //public void newLine()
              bw.newLine();
              bw.write("world");
              bw.newLine();
              bw.write("Java");
              bw.newLine();
              bw.write("newLine");
              bw.flush();
              bw.close();
          }
      }
      
      程序执行后,查看hello.txt信息为:
      Hello
      world
      Java
      newLine
      复制代码
    2. public String readLine():

      public class BufferedTest5 {
          public static void main(String[] args) throws IOException {
              BufferedReader br = new BufferedReader(new FileReader("e:\\demo\\hello.txt"));
      
              //定义获取字符串
              String line = null;
              while((line = br.readLine()) != null) {
                  System.out.println(line);
              }
              br.close();
          }
      }
      
      程序执行后,查看hello.txt信息为:
      Hello
      world
      Java
      newLine
      复制代码

文本排序案例

文本排序练习什么呢,下面我分享我比较喜欢的一首,然后按一定的顺序乱排。当然不要担心,肯定有办法让你可以重新排序的。

8.既然目标是地平线
7.我不去想身后会不会袭来寒风冷雨
3.便只顾风雨兼程
9.留给世界的只能是背影
4.我不去想能否赢得爱情
5.既然钟情于玫瑰
6.就勇敢地吐露真诚
10.我不去想未来是平坦还是泥泞
11.只要热爱生命
12.一切,都在意料之中
5.既然钟情于玫瑰
6.就勇敢地吐露真诚
1.我不去想是否能够成功
2.既然选择了远方
复制代码

<热爱生命>来自汪国真的诗集里头,说实话,这篇我在高中的作文中,一直被我拿来引用。不止这一篇吧,还有汪国真的<感谢>等。虽然作文至今也没有拿到特别高的分,但也不能妨碍我对汪国真的作品的热爱。正如我原想收获一缕春风,你却给了我整个夏天。你要相信你的才华不会永远被埋没。所以当我们跨越了一座高山,也就是跨越了一个真实的自己。

好了,Stop,说回重点吧:我们要如何把这篇诗排序呢。

分析:

  • 我们不是刚学了一个字符缓冲流中读取一行的吗,那我们就可以先读取每一行文字。

  • 然后不得去解析文字吗,我们用前一两位排?但是如果我文本一多,有几百行呢,不就是三位数了吗,显示这种方法不可取的。

  • 我们应该可以看到在序号的后面有个字符".",我们可以用这个缺陷来入手。然后将拆分后的序号和文字存储到集合中,这种有CP组合的,我们就可以采用Map集合啦。

  • 然后再将排好序的文字,重新写入到文件中把。

  • OK,既然知道了思路,我们就一起大干一场吧:

    public class BufferedTest6 {
        public static void main(String[] args) throws IOException {
            //创建字符输入流对象
            BufferedReader br = new BufferedReader(new FileReader("e:\\demo\\热爱生命.txt"));
    
            //创建字符输出流对象
            BufferedWriter bw = new BufferedWriter(new FileWriter("e:\\demoCopy\\热爱生命.txt"));
    
            //创建Map集合,用于保存将来得到的数据,键为序号,值为文本。
            HashMap<String, String> map = new HashMap<>();
    
            //读取数据
            String line = null;
            while((line = br.readLine()) != null) {
                //通过.来分割文本和序号
                String[] split = line.split("\\.");
                //将得到的数据存入集合中
                map.put(split[0], split[1]);
            }
    
            //接下来不就是可以按序号即键找值的方式来得到文本吗
            for (int i = 1; i <= map.size(); i++) {
                String key = String.valueOf(i);
                //键找值
                String value = map.get(key);
    
                //得到文本,这里我序号还是写进去,怕你觉得我懵你,如果相信我的同学,可以不用写序号了。
                bw.write(key + "." + value);
                if(i == 7){
                    System.out.println(value);
                }
                //bw.write(value);
                bw.newLine();
                bw.flush();
            }
            bw.flush();
            //释放资源
            bw.close();
            br.close();
        }
    }
    复制代码

    程序运行后,正如一切都在意料之中,查看文件可以看到:

    1.我不去想是否能够成功
    2.既然选择了远方
    3.便只顾风雨兼程
    4.我不去想能否赢得爱情
    5.既然钟情于玫瑰
    6.就勇敢地吐露真诚
    7.我不去想身后会不会袭来寒风冷雨
    8.既然目标是地平线
    9.留给世界的只能是背影
    10.我不去想未来是平坦还是泥泞
    11.只要热爱生命
    12.一切,都在意料之中
    复制代码

    如果不想有序号了,可以选择不写入序号。

总结

相信各位看官都对IO流中高级流中的缓冲流类有了一定了解,期待等待下一章的高级流——转换流教学吧!

当然还有很多流等着下次一起看吧!欢迎期待下一章的到来!

学到这里,今天的世界打烊了,晚安!虽然这篇文章完结了,但是我还在,永不完结。我会努力保持写文章。来日方长,何惧车遥马慢!

感谢各位看到这里!愿你韶华不负,青春无悔!

注: 如果文章有任何错误和建议,请各位大佬尽情留言!如果这篇文章对你也有所帮助,希望可爱亲切的您给个三连关注下,非常感谢啦!


作者:***爷哪吒
链接:https://juejin.cn/post/6994718043582496805
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。