• Java进阶(十一)缓冲流


    十一、缓冲流

    需要学会什么?

    • 缓冲流:之前学习的流都属于基础流,性能不是最好的,缓冲流读写数据的性能更好。
    • 转换流:使用字符流读取中文不会乱码原始是什么?如果读取的文件编码与代码编码不一致怎么办?
    • 序列化:如何把Java对象进行长久保存。
    • 打印流:开发中有一种使用极为方便、性能高效的写数据的流,使用很多。
    • IO框架等:IO流原生的API使用起来其实挺麻烦的,有没有更好用的方式。

    1.缓冲流

    a.缓冲流概述

    缓冲流概述:

    • 缓冲流也称为高效流、或者高级流。之前学习的字节流可以称为原始流。
    • 作用:缓冲流自带缓冲区、可以提高原始字节流、字符流读写数据的性能。
      在这里插入图片描述
    字节缓冲输入流
    字节缓冲输出流
    字符缓冲输入流
    字符缓冲输出流
    IO流体系
    字节流
    字符流
    InputStream-抽象类
    FileInputStream-实现类
    BufferedInputStream-实现类
    OutputStream-抽象类
    FileOutputStream-实现类
    BufferedOutputStream-实现类
    Reader-抽象类
    FileReader-实现类
    BufferedReader-实现类
    Writer-抽象类
    FileWriter-实现类
    BufferedWriter-实现类

    总结:

    1. 缓冲流的作用?
      • 缓冲流自带缓冲区、可以提高原始字节流、字符流读写数据的性能。
    2. 缓冲流有几种?
      • 字节缓冲流:
        • 字节缓冲输入流:BufferedInputStream。
        • 字节缓冲输出流:BufferedOutputStream。
      • 字符缓冲流:
        • 字符缓冲输入流:BufferedReader。
        • 字符缓冲输出流:BufferedWriter。

    b.字节缓冲流

    字节缓冲流性能优化原理:

    • 字节缓冲输入流自带了8KB缓冲池,以后我们直接从缓冲池读取数据,所以性能较好。
    • 字节缓冲输出流自带了8KB缓冲池,数据就直接写入到缓冲池中去,写数据性能极高。

    Test.java

    import java.io.*;
    
    public class Test {
        public static void main(String[] args) throws Exception {
            try (
                    // 创建一个字节流输入管道与原文件接通
                    InputStream inputStream = new FileInputStream("day11-oop-demo/src/oldData.txt");
    
                    // 把原始的字节输入流管道包装成高级的缓冲字节输入流
                    InputStream inputStreamBuffered = new BufferedInputStream(inputStream);
    
                    // 创建一个字节输出流管道于目标文件接通
                    OutputStream outputStream = new FileOutputStream("day11-oop-demo/src/newData.txt");
    
                    // 把字节输出流管道包装成高级的缓冲字节输出流管道
                    OutputStream outputStreamBuffered = new BufferedOutputStream(outputStream);
            ) {
                // 定义一个字节数组转移数据
                byte[] bytes = new byte[1024];
    
                // 记录每次读取的字节数
                int length;
    
                while ((length = inputStreamBuffered.read(bytes)) != -1) {
                    outputStreamBuffered.write(bytes, 0, length);
                }
    
                System.out.println("拷贝完成!");
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    总结:

    1. 字节缓冲流为什么提高了操作数据的性能?
      • 字节缓冲流自带8KB缓冲区。
      • 可以提高原始字节流、字符流读写数据的性能。
    2. 字节缓冲流的功能如何调用?
      • public BufferedOutputStrem(OutputStream os)
      • public BufferedInputStrem(InputStream is)

    c.字节缓冲流的性能分析

    1. 已经说明了字节缓冲流的性能高效,但是没有直观感受。
    2. 如何测试字节缓冲流的读写性能?

    分别使用不同的方式复制大视频观察性能情况。

    需求:

    • 分别使用低级字节流和高级字节缓冲流拷贝大视频,记录耗时。

    分析:

    1. 使用低级的字节流按照一个一个字节的形式复制文件。
    2. 使用低级的字节流按照一个一个字节数组的形式复制文件。
    3. 使用高级的缓冲字节流按照一个一个字节的形式复制文件。
    4. 使用高级的缓冲字节流按照一个一个字节数组的形式复制文件。

    Test.java

    import java.io.*;
    
    public class Test {
        private static final String SRC_FILE = "day11-oop-demo/src/可惜没如果.mp4";
        private static final String DEST_FILE = "day11-oop-demo/src/";
    
        public static void main(String[] args) {
            // 使用低级的字节流按照一个一个字节的形式复制文件
            copy1();
            // 使用低级的字节流按照一个一个字节数组的形式复制文件
            copy2();
            // 使用高级的缓冲字节流按照一个一个字节的形式复制文件
            copy3();
            // 使用高级的缓冲字节流按照一个一个字节数组的形式复制文件
            copy4();
        }
    
        /**
         * 使用高级的缓冲字节流按照一个一个字节数组的形式复制文件
         */
        private static void copy4() {
            long startTime = System.currentTimeMillis();
    
            try (
                    // 创建一个字节流输入管道与原文件接通
                    InputStream inputStream = new FileInputStream(SRC_FILE);
    
                    // 把原始的字节输入流管道包装成高级的缓冲字节输入流
                    InputStream inputStreamBuffered = new BufferedInputStream(inputStream);
    
                    // 创建一个字节输出流管道于目标文件接通
                    OutputStream outputStream = new FileOutputStream(DEST_FILE + "video4.mp4");
    
                    // 把字节输出流管道包装成高级的缓冲字节输出流管道
                    OutputStream outputStreamBuffered = new BufferedOutputStream(outputStream);
            ) {
                // 定义一个字节数组转移数据
                byte[] bytes = new byte[1024];
    
                // 记录每次读取的字节数
                int length;
    
                while ((length = inputStreamBuffered.read(bytes)) != -1) {
                    outputStreamBuffered.write(bytes, 0, length);
                }
    
                System.out.println("拷贝完成!");
    
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            long endTime = System.currentTimeMillis();
    
            System.out.println("使用高级的缓冲字节流按照一个一个字节数组的形式复制文件:" + (endTime - startTime) / 1000.0 + "秒");
        }
    
        /**
         * 使用高级的缓冲字节流按照一个一个字节的形式复制文件
         */
        private static void copy3() {
            long startTime = System.currentTimeMillis();
    
            try (
                    // 创建一个字节流输入管道与原文件接通
                    InputStream inputStream = new FileInputStream(SRC_FILE);
    
                    // 把原始的字节输入流管道包装成高级的缓冲字节输入流
                    InputStream inputStreamBuffered = new BufferedInputStream(inputStream);
    
                    // 创建一个字节输出流管道于目标文件接通
                    OutputStream outputStream = new FileOutputStream(DEST_FILE + "video3.mp4");
    
                    // 把字节输出流管道包装成高级的缓冲字节输出流管道
                    OutputStream outputStreamBuffered = new BufferedOutputStream(outputStream);
            ) {
    
                // 定义一个变量记录每次读取的字节
                int b;
    
                while ((b = inputStreamBuffered.read()) != -1) {
                    outputStreamBuffered.write(b);
                }
    
                System.out.println("拷贝完成!");
    
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            long endTime = System.currentTimeMillis();
    
            System.out.println("使用高级的缓冲字节流按照一个一个字节的形式复制文件:" + (endTime - startTime) / 1000.0 + "秒");
    
        }
    
        /**
         * 使用低级的字节流按照一个一个字节数组的形式复制文件
         */
        private static void copy2() {
            long startTime = System.currentTimeMillis();
    
            try (
                    // 创建低级的字节输入流于源文件接通
                    InputStream inputStream = new FileInputStream(SRC_FILE);
    
                    // 创建低级的字节输出流与目标文件接通
                    OutputStream outputStream = new FileOutputStream(DEST_FILE + "video2.mp4")
            ) {
    
                // 定义一个变量记录每次读取的字节
                byte[] bytes = new byte[1024];
    
                // 记录每次读取的字节数
                int length;
    
                while ((length = inputStream.read(bytes)) != -1) {
                    outputStream.write(bytes, 0, length);
                }
    
                System.out.println("拷贝完成!");
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            long endTime = System.currentTimeMillis();
    
            System.out.println("使用低级的字节流按照一个一个字节数组的形式复制文件:" + (endTime - startTime) / 1000.0 + "秒");
        }
    
        /**
         * 使用低级的字节流按照一个一个字节的形式复制文件
         */
        private static void copy1() {
            long startTime = System.currentTimeMillis();
    
            try (
                    // 创建低级的字节输入流于源文件接通
                    InputStream inputStream = new FileInputStream(SRC_FILE);
    
                    // 创建低级的字节输出流与目标文件接通
                    OutputStream outputStream = new FileOutputStream(DEST_FILE + "video1.mp4")
            ) {
    
                // 定义一个变量记录每次读取的字节
                int b;
    
                while ((b = inputStream.read()) != -1) {
                    outputStream.write(b);
                }
                System.out.println("拷贝完成!");
    
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            long endTime = System.currentTimeMillis();
    
            System.out.println("使用低级的字节流按照一个一个字节的形式复制文件耗时:" + (endTime - startTime) / 1000.0 + "秒");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162

    程序输出结果:

    拷贝完成!
    使用低级的字节流按照一个一个字节的形式复制文件耗时:529.098秒
    拷贝完成!
    使用低级的字节流按照一个一个字节数组的形式复制文件:1.149秒
    拷贝完成!
    使用高级的缓冲字节流按照一个一个字节的形式复制文件:11.206秒
    拷贝完成!
    使用高级的缓冲字节流按照一个一个字节数组的形式复制文件:0.598秒
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    总结:

    1. 推荐使用哪种方式提高字节流读写数据的性能?
      • 建议使用字节缓冲输入流、字节缓冲输出流,结合字节数组的方法,目前来看是性能最优的组合。

    d.字符缓冲流

    字符缓冲输入流:

    • 字符缓冲输入流:BufferedReader。
    • 作用:提高字符输入流读取数据的性能,除此之外多了按照行读取数据的功能。
    构造器说明
    public BufferedReader(Reader r)可以把低级的字符输入流包装成一个高级的缓冲字符输入流管道,从而提高字符输入流读数据的性能。

    Test.java

    import java.io.BufferedReader;
    import java.io.FileReader;
    import java.io.Reader;
    
    /**
     * 目标:学会使用缓冲字符输入流提高字符输入流的性能,新增了按照行读取的方法(经典代码)
     */
    public class Test {
        public static void main(String[] args) {
            try (
                    // 创建一个文件字符输入流与源文件接通
                    Reader reader = new FileReader("day11-oop-demo/src/oldData.txt");
    
                    // 把低级的字符输入流包装成高级的缓冲字符输入流
                    BufferedReader bufferedReader = new BufferedReader(reader);
            ) {
    //            char[] chars = new char[1024];
    //
    //            int len;
    //
    //            while ((len = bufferedReader.read(chars)) != -1) {
    //                String string = new String(chars, 0, len);
    //                System.out.println(string);
    //            }
    
                // 读一行
    //            System.out.println(bufferedReader.readLine());
    
                /* 经典代码 */
                String line;
                while ((line = bufferedReader.readLine()) != null) {
                    System.out.println(line);
                }
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    字符缓冲输出流:

    • 字符缓冲输出流:BufferedWriter。
    • 作用:提高字符输出流写数据的性能,除此之外多了换行功能。
    构造器说明
    public BufferedWriter(Writer w)可以把低级的字符输入流包装成一个高级的缓冲字符输出流管道,从而提高字符输出流写数据的性能。
    方法说明
    public void newLine()换行操作。

    Test.java

    import java.io.BufferedWriter;
    import java.io.FileWriter;
    import java.io.Writer;
    
    /**
     * 目标:缓冲字符输出流管道的使用,学会它多出来多一个功能:newLine()
     */
    public class Test {
        public static void main(String[] args) throws Exception{
            // 创建一个字符输出流管道与目标文件接通
            Writer writer = new FileWriter("day11-oop-demo/src/newData.txt");
            // 追加
            //Writer writer = new FileWriter("day11-oop-demo/src/newData.txt", true);
    
            BufferedWriter bufferedWriter = new BufferedWriter(writer);
    
            bufferedWriter.write("Java");
            bufferedWriter.write("Python");
            bufferedWriter.newLine();
    
            bufferedWriter.flush();
            bufferedWriter.close();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    总结:

    1. 字符缓冲流为什么提高了操作数据的性能?
      • 字符缓冲流自带8K缓冲区。
      • 可以提高原始字节流读写数据的性能。
    2. 字符缓冲流的功能如何使用?
      • public BufferedReader(Reader r)
      • 性能提升了,多了readLine()按照行读写的功能。
      • public BufferedWriter(Writer w)
      • 性能提升了,多了newLine()换行的功能。

    案例:拷贝出师表到另一个文件,恢复顺序。

    需求:

    • 把《出师表》的文章顺序进行恢复到一个新文件中。

      一、先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。
      九、今当远离,临表涕零,不知所言。
      二、宫中府中,俱为一体,陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不宜偏私,使内外异法也。
      八、愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏,臣不胜受恩感激。
      四、将军向宠,性行淑均,晓畅军事,试用于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。
      三、侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉以咨之,然后施行,必能裨补阙漏,有所广益。
      六、臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。
      五、亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。
      七、先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐托付不效,以伤先帝之明,故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9

    分析:

    1. 定义一个缓冲字符输入流管道与源文件接通。
    2. 定义一个List集合存储读取的每行数据。
    3. 定义一个循环按照行读取数据,存入到List集合中去。
    4. 对List集合中的每行数据按照首字符编号升序排序。
    5. 定义一个缓冲字符输出管道与目标文件接通。
    6. 遍历List集合中的每个元素,用缓冲输出管道写出并换行。

    Test.java

    import java.io.*;
    import java.util.*;
    
    public class Test {
        public static void main(String[] args) {
    
            try (
                    // 创建缓冲字符输入流管道与源文件接通
                    BufferedReader bufferedReader = new BufferedReader(new FileReader("day11-oop-demo/src/出师表-乱序.txt"));
    
                    // 定义缓冲字符输出管道与目标文件接通
                    BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("day11-oop-demo/src/出师表-正序.txt"));
            ){
                /* 读取内容 */
                // 定义一个List集合存储每行内容
                List<String> list = new ArrayList<>();
    
                // 定义循环 按照行读取文章
                String line;
                while ((line = bufferedReader.readLine()) != null) {
                    list.add(line);
                }
    
                /* 排序 */
                // 自定义排序规则
                List<String> sizes = new ArrayList<>();
                // 按照大小加入
                Collections.addAll(sizes, "一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一", "十二");
    
                Collections.sort(list, new Comparator<String>() {
                    @Override
                    public int compare(String s1, String s2) {
                        // 获取索引比较大小
                        return sizes.indexOf(s1.substring(0, s1.indexOf("、"))) - sizes.indexOf(s2.substring(0, s2.indexOf("、")));
                    }
                });
    
                System.out.println(list);
    
                /* 写入 */
    
                // 遍历集合中的每行文字写入文件中
                for (String s : list) {
                    bufferedWriter.write(s);
    
                    // 换行
                    bufferedWriter.newLine();
                }
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    2.转换流

    1. 之前我们使用字符流读取中文是否乱码?
      • 没有乱码,因为代码编码和文件编码都是UTF-8。
    2. 如果代码编码和文件编码不一致,使用字符流直接读取还能不乱码吗?
      • 会乱码。
      • 文件编码和读取的编码必须一致才不会乱码。

    a.问题引出:不同编码读取乱码问题

    使用相同编码读取不同编码的文件内容:

    需求:分别使用如下两种方式读取文件内容。

    • 代码编码是UTF-8,文件编码也是UTF-8,使用字符流读取观察输出的中文字符结果。
    • 代码编码是UTF-8,文件编码使用GBK,使用字符流读取观察输出的中文字符结果。

    CharSetDemo.java

    import java.io.BufferedReader;
    import java.io.FileReader;
    import java.io.Reader;
    
    /**
     * 演示一下代码编码与文件编码相同和不同的情况
     */
    public class CharSetDemo {
        public static void main(String[] args) {
            try (
                    /* 代码: UTF-8 文件: UTF-8 不会乱码 */
                    // 创建一个文件字符输入流与源文件接通
                    Reader reader = new FileReader("day11-oop-demo/src/utf-8Data.txt");
    
                    /* 代码: UTF-8 文件: GBK 乱码 */
                    Reader reader1 = new FileReader("day11-oop-demo/src/gbkData.txt");
    
                    // 把低级的字符输入流包装成高级的缓冲字符输入流
                    BufferedReader bufferedReader = new BufferedReader(reader);
    
                    BufferedReader bufferedReader1 = new BufferedReader(reader1);
            ) {
                /* 经典代码 */
                String line;
                while ((line = bufferedReader.readLine()) != null) {
                    System.out.println(line);
                }
    
                String line1;
                while ((line1 = bufferedReader1.readLine()) != null) {
                    System.out.println(line1);
                }
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    b.字符输入转换流

    1. 如果代码编码和文件编码不一致,使用字符流直接读取还能不乱码吗?

      • 会乱码。
    2. 如何解决?

      • 使用字符输入转换流

      • 可以提取文件(GBK)的原始字节流,原始字节不会存在问题。

      • 然后把字节流以指定编码转换成字符输入流,这样字节输入流中的字符就不乱码了。

    字节缓冲输入流
    字节缓冲输出流
    字符缓冲输入流
    字符输入转换流
    字符缓冲输出流
    字符输出转换流
    IO流体系
    字节流
    字符流
    InputStream-抽象类
    FileInputStream-实现类
    BufferedInputStream-实现类
    OutputStream-抽象类
    FileOutputStream-实现类
    BufferedOutputStream-实现类
    Reader-抽象类
    FileReader-实现类
    BufferedReader-实现类
    InputStreamReader-实现类
    Writer-抽象类
    FileWriter-实现类
    BufferedWriter-实现类
    OutputStreamWriter-实现类

    字符输入转换流:

    • InputStreamReader,可以把原始的字节流按照指定编码转换成字符输入流。
    构造器说明
    public InputStreamReader(InputStream is0)可以把原始的字节流按照代码默认编码转换成字符输入流。几乎不用,与默认的FileReader一样。
    public InputStreamReader(InputStream is, String chartSet)可以把原始的字节流按照指定编码转换成字符输入流,这样字符流中的字符就不乱码了。(重点)

    InputStreamReaderDemo.java

    import java.io.*;
    
    public class InputStreamReaderDemo {
        public static void main(String[] args) throws Exception {
    
            // 提取GBK文件的原始字节流
            InputStream inputStream = new FileInputStream("day11-oop-demo/src/gbkData.txt");
    
            // 把原始字节流以指定GBK编码转换成字符输入流
            Reader reader = new InputStreamReader(inputStream, "GBK");
    
            // 把低级的字符输入流包装成高级的缓冲字符输入流
            BufferedReader bufferedReader = new BufferedReader(reader);
            
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                System.out.println(line);
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    总结:

    1. 字符输入转换流InputStreamReader作用:
      • 可以解决字符流读取不同编码乱码的问题。
      • public InputStreamReader(InputStream is, String charset) 可以指定编码把原始字节流转换成字符流,如此字符流中的字符不乱码。

    c.字符输出转换流

    1. 如果需要控制写出去的字符使用的编码,怎么办?

      • 可以把字符以指定编码获取字节后再使用字节输出流写出去

        "我爱中国".getBytes(编码)
        
        • 1
      • 可以使用字符输出转换流实现。

    字符输出转换流:

    • 字符输出转换流:OutputStreamWriter,可以把字节输出流按照制定编码转换成字符输出流。
    构造器说明
    public OutputStreamWriter(OutputStream os)可以把原始的字节输出流按照代码默认编码转换成字符输出流,几乎不用。
    public OutputStreamWriter(OutputStream os, String charset)可以把原始的字节输出流按照指定编码转换成字符输出流(重点)。

    OutputStreamWriterDemo.java

    import java.io.*;
    
    public class OutputStreamWriterDemo {
        public static void main(String[] args) throws Exception {
            // 定义一个字节输出流
            OutputStream outputStream = new FileOutputStream("day11-oop-demo/src/gbkData.txt");
    
            // 把原始的字节输出流转换成字符输出流
            // 以默认的UTF-8写字符出去 跟直接用FileWriter一样
            // Writer writer = new OutputStreamWriter(outputStream);
            // 指定GBK编码写字符
            Writer writer = new OutputStreamWriter(outputStream, "GBK");
    
            // 把低级的缓冲输出流包装成高级的缓冲字符输出流
            BufferedWriter bufferedWriter = new BufferedWriter(writer);
    
            // 写入数据
            bufferedWriter.write("我爱中国,一");
            bufferedWriter.newLine();
            bufferedWriter.write("我爱中国,二");
            bufferedWriter.newLine();
            bufferedWriter.write("我爱中国,三");
    
            // 刷新
            bufferedWriter.flush();
    
            // 关闭流
            bufferedWriter.close();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    总结:

    1. 字符输出转换流OutputStreamWriter的作用?
      • public OutputStreamWriter(OutputStream os, String charset)
      • 可以指定编码把字节输出流转化成字符输出流,从而可以指定写出去的字符编码。

    3.序列化对象

    字节缓冲输入流
    对象字节输入流
    字节缓冲输出流
    对象字节输出流
    字符缓冲输入流
    字符输入转换流
    字符缓冲输出流
    字符输出转换流
    IO流体系
    字节流
    字符流
    InputStream-抽象类
    FileInputStream-实现类
    BufferedInputStream-实现类
    ObjectInputStream-实现类
    OutputStream-抽象类
    FileOutputStream-实现类
    BufferedOutputStream-实现类
    ObjectOutputStream-实现类
    Reader-抽象类
    FileReader-实现类
    BufferedReader-实现类
    InputStreamReader-实现类
    Writer-抽象类
    FileWriter-实现类
    BufferedWriter-实现类
    OutputStreamWriter-实现类

    a.对象序列化

    对象序列化:

    • 作用:以内存为基准,把内存中的对象存储到磁盘文件中去,称为对象序列化。
    • 使用到的流是对象字节输出流:ObjectOutputStream。
    对象字节输出流,写对象数据到磁盘文件
    内存
    磁盘

    对象序列化:

    • 使用到的流是对象字节输出流:ObjectOutputStream。
    构造器说明
    public ObjectOutpuStream(OutputStream out)把低级字节输出流包装成高级的对象字节输出流。
    方法名称说明
    public final void writeObject(Object obj)把对象写出去到对象序列化流的文件中去。

    Student.java

    import java.io.Serial;
    import java.io.Serializable;
    
    /**
     * 对象如果要序列化 必须实现Serializable序列化接口
     */
    public class Student implements Serializable {
        // 申明序列化的版本号码
        // 序列化的版本号与反序列化的版本号必须一致才不会出错
        @Serial
        private static final long serialVersionUID = 1;
        private String name;
        private char sex;
        // transient修饰的成员变量不参与序列化
        private transient int age;
    
        public Student() {
        }
    
        public Student(String name, char sex, int age) {
            this.name = name;
            this.sex = sex;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public char getSex() {
            return sex;
        }
    
        public void setSex(char sex) {
            this.sex = sex;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", sex='" + sex + '\'' +
                    ", age='" + age + '\'' +
                    '}';
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    ObjectOutputStreamDemo.java

    package com.javase.serializable;
    
    import java.io.FileOutputStream;
    import java.io.ObjectOutputStream;
    
    /**
     * 目标:学会对象序列化,使用ObjectOutputStream把内存中的对象存入到磁盘文件中
     *
     */
    public class ObjectOutputStreamDemo {
        public static void main(String[] args) throws Exception {
            // 创建学生对象
            Student student = new Student("花千骨", '女', 16);
    
            // 对象序列化:使用对象字节输出流包装字节输出流管道
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("day12-oop-demo/src/obj.txt"));
    
            // 直接调用序列化方法
            objectOutputStream.writeObject(student);
    
            // 释放资源
            objectOutputStream.close();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    b.对象反序列化

    对象反序列化:

    • 使用到的流是对象字节输入流:ObjectInputStream。
    • 作用:以内存为基准,把存储到磁盘文件中去的对象数据恢复成内存中的对象,称为对象反序列化。
    对象字节输出流,写对象数据到磁盘文件
    磁盘
    内存
    构造器说明
    public ObjectInputStream(InputStream out)把低级字节输入流包装成高级的对象字节输入流。
    方法名称说明
    public Object readObject()把存储到磁盘文件中去的对象数据恢复成内存中的对象返回。

    ObjectInputStreamDemo.java

    import java.io.FileInputStream;
    import java.io.ObjectInputStream;
    
    /**
     * 目标:学会进行对象反序列化 使用对象字节输入流把文件中的对象数据恢复成内存中的Java对象
     */
    public class ObjectInputStreamDemo {
        public static void main(String[] args) throws Exception{
            // 创建对象字节输入流管道包装低级的字节流输入流管道
            ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("day12-oop-demo/src/obj.txt"));
    
            // 调用字节输入流的反序列化方法
            Student student = (Student) objectInputStream.readObject();
    
            // 关闭流
            objectInputStream.close();
    
            System.out.println(student);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    4.打印流

    字节缓冲输入流
    对象字节输入流
    字节缓冲输出流
    对象字节输出流
    字节打印流
    字符缓冲输入流
    字符输入转换流
    字符缓冲输出流
    字符输出转换流
    字符打印流
    IO流体系
    字节流
    字符流
    InputStream-抽象类
    FileInputStream-实现类
    BufferedInputStream-实现类
    ObjectInputStream-实现类
    OutputStream-抽象类
    FileOutputStream-实现类
    BufferedOutputStream-实现类
    ObjectOutputStream-实现类
    PrintStream-实现类
    Reader-抽象类
    FileReader-实现类
    BufferedReader-实现类
    InputStreamReader-实现类
    Writer-抽象类
    FileWriter-实现类
    BufferedWriter-实现类
    OutputStreamWriter-实现类
    PrintWriter-实现类

    打印流:

    • 作用:打印流可以实现方便、高效的打印数据到文件中去。打印流一般是指:PrintStream、PrintWriter两个类。
    • 可以实现打印什么数据就是什么数据,例如打印整数97写出去就是97,打印boolean的true,写出去就是true。

    a.PrintStream、PrintWriter

    PrintStream:

    构造器说明
    public PrintStream(OutputStream os)打印流直接通向字节输出流管道。
    public PrintStream(File f)打印流直接通向文件对象。
    public PrintStream(String filepath)打印流直接通向文件路径。

    Test.java

    import java.io.FileOutputStream;
    import java.io.PrintStream;
    
    public class Test {
        public static void main(String[] args) throws Exception{
    
            // 创建一个打印流对象
            PrintStream printStream = new PrintStream("day12-oop-demo/src/printstream.txt");
            // 基于低级管道 追加写入
            // PrintStream printStream = new PrintStream(new FileOutputStream("day12-oop-demo/src/printstream.txt", true));
            // 指定字符编码格式
            // PrintStream printStream = new PrintStream("day12-oop-demo/src/printstream.txt", "GBK");
    
            printStream.println(1);
            printStream.println('a');
            printStream.println("我爱中国");
            printStream.println(true);
            printStream.println("打印流");
    
            // 关闭流
            printStream.close();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    PrintWriter:

    构造器说明
    public PrintWriter(OutputStream os)打印流直接通向字节输出流管道。
    public PrintWriter(Writer w)打印流直接通向字符输出流管道。
    public PrintWriter(File f)打印流直接通向文件对象。
    public PrintWriter(String filepath)打印流直接通向文件路径。
    方法说明
    public void print(Xxx xx)打印任意类型的数据出去。

    Test.java

    import java.io.PrintStream;
    import java.io.PrintWriter;
    
    public class Test {
        public static void main(String[] args) throws Exception {
            // 创建一个打印流对象
            PrintWriter printWriter = new PrintWriter("day12-oop-demo/src/printWriter.txt");
            // PrintWriter printWriter = new PrintWriter("day12-oop-demo/src/printWriter.txt", "GBK");
    
            printWriter.println(1);
            printWriter.println('a');
            printWriter.println("我爱中国");
            printWriter.println(true);
            printWriter.println("打印流");
    
            // 关闭流
            printWriter.close();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    PrintStream和PrintWriter的区别:

    • 打印数据功能是一摸一样的,都是使用方便,性能高效(核心优势)。
    • PrintStream继承自字节输出流OutputStream,支持写字节数据的方法。
    • PrintWriter继承自字符输出流Writer,支持写字符数据出去。

    总结:

    1. 打印流有几种?各有什么特点?
      • 打印流一般是指:PrintStream、PrintWriter两个类。
      • 打印功能两者是一样的使用方式。
      • PrintStream继承自字节输出流OutputStream,支持写字节。
      • PrintWrite继承自字符输出流Writer,支持写字符。
    2. 打印流的优势是什么?
      • 两者在打印功能上都是使用方便、性能高效(核心优势)。

    b.输出语句的重定向

    • 属于打印流的一种应用,可以把输出语句的打印位置改到文件。

      PrintStream ps = new PrintStream("文件地址");
      
      System.setOUt(ps);
      
      • 1
      • 2
      • 3

    Test.java

    import java.io.PrintStream;
    
    /**
     * 目标:了解改变输出语句的位置到文件
     */
    public class Test {
        public static void main(String[] args) throws Exception {
            System.out.println("        饮酒        ");
            System.out.println("采菊东篱下,悠然见南山。");
            System.out.println("山气日夕佳,飞鸟相与还。");
    
            // 改变输出语句的位置(重定向)
            // 创建一个打印流对象
            PrintStream printStream = new PrintStream("day12-oop-demo/src/打印流重定向.txt");
            // 把系统打印流改成我们自己的打印流
            System.setOut(printStream);
    
            System.out.println("      西江月·自幼曾攻经史      ");
            System.out.println("心在山东身在吴,飘蓬江海谩嗟吁。");
            System.out.println("他日若遂凌云志,敢笑黄巢不丈夫。");
    
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    5.Properties

    Map 接口
    HashMap 实现类
    HashTable 实现类
    ... 接口
    LinkedHashMap 实现类
    Properties 实现类
    TreeMap 实现类

    Properties属性集对象:

    • 其实就是一个Map集合,但是我们一般不会当集合使用,因为HashMap更好用。

    Properties核心作用:

    • Properties代表的是一个属性文件,可以把自己对象中的键值对信息存入到一个属性文件中去。
    • 属性文件:后缀是.properties结尾的文件,里面的内容都是key=value,后续做系统配置信息的。

    Properties的API:

    • Properties和IO流结合的方法:

      构造器说明
      void load(InputStream isStream)从输入字节流读取属性列表(键和元素对)。
      void load(Reader reader)从输入字节流读取属性列表(键和元素对)。
      void store(OutputStream out, String comments)将此属性列表(键和元素对)写入此Properties表中,以适合于使用load(InputStream)方法的格式写入输出字节流。
      void store(Writer writer, String comments)将此属性列表(键和元素对)写入此Properites表中,以适合使用load(Reader)方法的格式写入输出字节流。
      public Object setProperty(String key, String value)保持键值对(put)。
      public String getProperty(String key)使用此属性列表中指定的键搜索属于值(get)。
      public Set stringPropertyNames()所有键的名称的集合(keySet())。

    PropertiesDemo.java

    import java.io.FileReader;
    import java.io.FileWriter;
    import java.util.Map;
    import java.util.Properties;
    
    public class PropertiesDemo {
        public static void main(String[] args) throws Exception{
    
            Properties properties = new Properties();
    
            // 存储键值对
            properties.put("admin", "123456");
            properties.setProperty("lucy", "666666");
            System.out.println(properties);
    
            // 存储到文件 此流不用关闭
            properties.store(new FileWriter("day12-oop-demo/src/users.properties"), "注释");
    
            // 加载属性文件中的键值对数据到属性对象properties中去
            properties.load(new FileReader("day12-oop-demo/src/users.properties"));
            System.out.println(properties);
            
            // 取出键对应的值
            String admin = properties.getProperty("admin");
            System.out.println(admin);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    6.IO框架

    commons-io框架:

    • commons-io是apache开源基金组织提供的一组有关IO操作的类库,可以提高IO功能开发的效率。
    • commons-io工具包提供了很多有关IO操作的类,有两个主要的类FileUtils、IOUtils。

    FileUtils主要有如下方法:

    方法名说明
    String readFileToString(File file, String encoding)读取文件中的数据,返回字符串。
    void copyFile(File srcFile, File destFile)复制文件。
    void copyDirectoryToDirectory(File srcDir, File destDir)复制文件夹。

    导入commons-io-2.11.0做开发

    需求:

    • 使用commons-io简化io流读写。

    分析:

    1. 在项目中创建一个文件夹:lib。
    2. 将commons-io-2.11.0.jar文件复制到lib文件夹。
    3. 在jar文件上点击右键,选择Add as Library -> 点击 OK。
    4. 在类中导包使用。

    Test.java

    import org.apache.commons.io.FileUtils;
    import org.apache.commons.io.IOUtils;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.nio.file.Files;
    import java.nio.file.Path;
    
    public class Test {
        public static void main(String[] args) throws Exception {
            // 1.文件复制
            IOUtils.copy(new FileInputStream("day12-oop-demo/src/obj.txt"), new FileOutputStream("day12-oop-demo/src/objNew.txt"));
    
            // 2.文件复制到某个文件夹下
            FileUtils.copyFileToDirectory(new File("day12-oop-demo/src/obj.txt"), new File("day12-oop-demo/src/commonsiodemo/"));
    
            // 3.文件夹复制到某个文件夹下
            FileUtils.copyDirectoryToDirectory(new File("day12-oop-demo/src/commonsiodemo/"), new File("day12-oop-demo/src/commonsiodemoNew/"));
    
            // 4.删除文件夹
            FileUtils.deleteDirectory(new File("day12-oop-demo/src/commonsiodemoNew/"));
    
            // JDK 1.7 也做了一些一行代码完成复制的操作:NIO
            Files.copy(Path.of("day12-oop-demo/src/commonsiodemo/"), Path.of("day12-oop-demo/src/commonsiodemoNIO/"));
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
           |
    
    • 1

    导入commons-io-2.11.0做开发

    需求:

    • 使用commons-io简化io流读写。

    分析:

    1. 在项目中创建一个文件夹:lib。
    2. 将commons-io-2.11.0.jar文件复制到lib文件夹。
    3. 在jar文件上点击右键,选择Add as Library -> 点击 OK。
    4. 在类中导包使用。

    Test.java

    import org.apache.commons.io.FileUtils;
    import org.apache.commons.io.IOUtils;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.nio.file.Files;
    import java.nio.file.Path;
    
    public class Test {
        public static void main(String[] args) throws Exception {
            // 1.文件复制
            IOUtils.copy(new FileInputStream("day12-oop-demo/src/obj.txt"), new FileOutputStream("day12-oop-demo/src/objNew.txt"));
    
            // 2.文件复制到某个文件夹下
            FileUtils.copyFileToDirectory(new File("day12-oop-demo/src/obj.txt"), new File("day12-oop-demo/src/commonsiodemo/"));
    
            // 3.文件夹复制到某个文件夹下
            FileUtils.copyDirectoryToDirectory(new File("day12-oop-demo/src/commonsiodemo/"), new File("day12-oop-demo/src/commonsiodemoNew/"));
    
            // 4.删除文件夹
            FileUtils.deleteDirectory(new File("day12-oop-demo/src/commonsiodemoNew/"));
    
            // JDK 1.7 也做了一些一行代码完成复制的操作:NIO
            Files.copy(Path.of("day12-oop-demo/src/commonsiodemo/"), Path.of("day12-oop-demo/src/commonsiodemoNIO/"));
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
  • 相关阅读:
    typescript55-泛型约束
    docker应用部署---Tomcat的部署配置
    人工智能-第三阶段-k近邻算法1-算法理论、kd树、鸢尾花数据
    如何构建DevOps管道:初学者指南
    1200*A. Trust Nobody(贪心)
    银行企业如何通过企微scrm解决用户获取和转化难题?
    【JavaEE】多线程(一)
    C++学习——C++函数的编译、成员函数的调用、this指针详解
    Redis——布隆过滤器
    P3381 【模板】最小费用最大流
  • 原文地址:https://blog.csdn.net/weixin_42856871/article/details/127706265