JavaSE——IO流3:高级流(字节字符缓冲流Buffered)

目录

一、字节缓冲输入流与字节缓冲输出流

(一)字节缓冲流拷贝文件——BufferedInputStream、BufferedOutputStream 

1.一次读写一个字节

2.一次读写一个字节数组

(二)字节缓冲流提高效率的原理

1.字节流与字节缓冲流的区别

2.缓冲区的作用

3.字节缓冲流的工作原理

(1)BufferedInputStream的工作原理

(2)BufferedOutputStream的工作原理

(3)缓冲流默认缓冲区大小与底层的数据类型

二、字符缓冲流

1.字符缓冲流的构造方法

2.字符缓冲流特有方法

3.字符缓冲输入流读取文件 

4.字符缓冲输出流写出文件

三、字节缓冲流和字符缓冲流综合练习(重要)

1.四种方式拷贝文件,并统计各自用时

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

(1)方式一:使用ArrayList

(2)方式二:使用TreeMap

3.控制软件运行次数

4.统计项目文件中,.java文件的总行数

四、IO流使用小贴士


字节缓冲输入流        BufferedInputStream

字节缓冲输出流        BufferedOutputStream

字符缓冲输入流        BufferedReader

字符缓冲输出流        BufferedWriter

一、字节缓冲输入流与字节缓冲输出流

        原理:底层自带了长度为8192的缓冲区提高性能。

        缓冲流是不能直接读取文件中的数据的,也不能直接把数据写到文件当中。在创建缓冲流对象的时候需要关联基本流,真正读写数据的,还是基本流。只不过有了缓冲流的加持,读写效率更高而已。

(一)字节缓冲流拷贝文件——BufferedInputStream、BufferedOutputStream 

1.一次读写一个字节

注意:只关闭缓冲流即可,因为关闭之前会默认把基本流关闭。

public class BufferedStreamDemo1 {
    public static void main(String[] args) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\test\\aaa\\movie.mp4"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("chapter18\\copy.mp4"));

        // 读取文件
        int len;
        while ((len = bis.read()) != -1) {
            bos.write(len);
        }

        bis.close();
        bos.close();
    }
}

2.一次读写一个字节数组

public class BufferedStreamDemo2 {
    public static void main(String[] args) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\test\\ccc\\图像1.jpg"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("chapter18\\copy图像1.jpg"));

        int len;
        byte[] bytes = new byte[1024];
        while ((len = bis.read(bytes)) != -1) {
            bos.write(bytes, 0, len);
        }
        bos.close();
        bis.close();
    }
}

(二)字节缓冲流提高效率的原理

        字节缓冲流(BufferedInputStream 和 BufferedOutputStream)在Java中通过使用内部缓冲区来提高 IO 操作的效率。其核心原理是减少对底层系统资源(如磁盘、网络等)的直接访问次数,从而提升整体的 IO 性能。下面详细解释其提高效率的原理:

1.字节流与字节缓冲流的区别

        字节流:InputStream和OutputStream以及它们的子类,如FileInputStream和FileOutputStream,用于字节级别的输入和输出操作。这些流在读取或写入每一个字节时都会直接与底层资源交互,例如磁盘或网络。

        字节缓冲流:BufferedInputStream和BufferedOutputStream是InputStream和OutputStream的包装类,它们通过内部的缓冲区一次性读取或写入大量数据,从而减少底层资源的访问频率。

2.缓冲区的作用

        缓冲区是一个临时存储数据的内存区域。字节缓冲流在读取或写入数据时,会先将数据放入缓冲区,而不是每次操作都直接访问磁盘或网络。

        减少系统调用:直接读取或写入文件系统或网络资源是相对较慢的操作,每次 IO 操作都会涉及到昂贵的系统调用。字节缓冲流通过一次性读取或写入较大块的数据,将这些操作集中处理,从而减少了系统调用的次数。

3.字节缓冲流的工作原理

(1)BufferedInputStream的工作原理

        当调用 read()方法时,BufferedInputStream 会先从它的缓冲区中读取数据。

        如果缓冲区为空(或用尽),BufferedInputStream会通过底层输入流(如 FileInputStream)从磁盘一次性读取一大块数据到缓冲区中。

        这样,当后续读取数据时,直接从内存中的缓冲区中获取数据,而不需要每次都访问磁盘,显著提高了读取效率。

(2)BufferedOutputStream的工作原理

        当调用write()方法时,BufferedOutputStream不会立即将数据写入底层输出流(如 FileOutputStream)。相反,它会先将数据写入内部的缓冲区。

        当缓冲区满了,或者调用flush()方法时,缓冲区中的数据才会一次性写入底层输出流。这样避免了每次写入一个字节就进行一次系统调用的情况,从而提高了写入效率。

(3)缓冲流默认缓冲区大小与底层的数据类型

        缓冲流自带了长度为8192的缓冲区,可以通过构造函数手动指定缓冲区的大小:

BufferedInputStream bis = new BufferedInputStream(new FileInputStream("input.txt"), 16384); // 16 KB 缓冲区
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("output.txt"), 16384); // 16 KB 缓冲区

        字节缓冲流的底层是byte类型,长度是8KB:

        字符缓冲流的底层是char类型,长度是16KB:

        每个字符占用2个字节,所以是16KB的字节空间

二、字符缓冲流

原理:底层自带了长度为8192的缓冲区提高性能。

        字符流的基本流当中,底层已经有了缓冲区了,因此,字符缓冲流对于字符流提升效率不明显,对于字符缓冲流而言,关键点是两个特有的方法(readLine()和newLine())。

1.字符缓冲流的构造方法

2.字符缓冲流特有方法

方法名称说明
public String readLine()

字符缓冲输入流特有方法

读取一行数据,如果读到文件末尾, 没有数据可读了,会返回null

readLine方法在读取的时候,一次读一整行,遇到回车换行结束

但是它不会把回车换行读到内存当中

所以使用System.out.println进行手动换行,否则文件中所有数据会打印在一行

public void newLine()字符缓冲输出流特有方法 跨平台的换行

3.字符缓冲输入流读取文件 

public class BufferedStreamDemo3 {
    public static void main(String[] args) throws IOException {
        // 字符缓冲输入流
        BufferedReader br = new BufferedReader(new FileReader("chapter18\\d.txt"));
        String line;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
        br.close();
    }
}

运行结果:

4.字符缓冲输出流写出文件

public class BufferedStreamDemo4 {
    public static void main(String[] args) throws IOException {
        // 开启续写
        BufferedWriter bw = new BufferedWriter(new FileWriter("chapter18\\g.txt", true));
        bw.write("我爱北京天安门,");
        bw.newLine();
        bw.write("天安门上太阳升。");
        bw.newLine();
        bw.close();
    }
}

运行结果:

三、字节缓冲流和字符缓冲流综合练习(重要)

1.四种方式拷贝文件,并统计各自用时

public class TestDemo4 {
    public static void main(String[] args) throws IOException {
        test1();
        test2();
        test3();
        test4();       
    }

    public static void test1() throws IOException {
        FileInputStream fis = new FileInputStream("D:\\test\\aaa\\movie.mp4");
        FileOutputStream fos = new FileOutputStream("D:\\test\\aaa\\bbb\\ccc\\movie1.mp4");
        long start = System.currentTimeMillis();
        int len;
        while ((len = fis.read()) != -1) {
            fos.write(len);
        }
        fis.close();
        long end = System.currentTimeMillis();
        long time = end - start;
        System.out.println("test1持续时间:" + time + "毫秒");
    }

    public static void test2() throws IOException {
        FileInputStream fis = new FileInputStream("D:\\test\\aaa\\movie.mp4");
        FileOutputStream fos = new FileOutputStream("D:\\test\\aaa\\bbb\\ccc\\movie2.mp4");
        long start = System.currentTimeMillis();
        int len;
        byte[] bytes = new byte[8192];
        while ((len = fis.read(bytes)) != -1) {
            fos.write(bytes, 0, len);
        }
        fis.close();
        long end = System.currentTimeMillis();
        long time = end - start;
        System.out.println("test2持续时间:" + time + "毫秒");
    }

    public static void test3() throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\test\\aaa\\movie.mp4"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\test\\aaa\\bbb\\ccc\\movie3.mp4"));
        long start = System.currentTimeMillis();
        int len;
        while ((len = bis.read()) != -1) {
            bos.write(len);
        }
        bis.close();
        long end = System.currentTimeMillis();
        long time = end - start;
        System.out.println("test3持续时间:" + time + "毫秒");
    }

    public static void test4() throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\test\\aaa\\movie.mp4"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\test\\aaa\\bbb\\ccc\\movie4.mp4"));
        long start = System.currentTimeMillis();
        int len;
        byte[] bytes = new byte[8192];
        while ((len = bis.read(bytes)) != -1) {
            bos.write(bytes, 0, len);
        }
        bis.close();
        long end = System.currentTimeMillis();
        long time = end - start;
        System.out.println("test4持续时间:" + time + "毫秒");
    }
}

运行结果:

因此,文件的拷贝可以使用一次读写一个字节数组相对较快。

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

csb.txt文件

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

        ArrayList<String> list = new ArrayList<>();
        String len;
        // 读取文件内容,直到文件末尾
        while ((len = br.readLine()) != null) {
            list.add(len);
        }
        br.close();
        
        System.out.println(list);
        
        // 集合中的元素排序
        list.sort(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                int split1 = Integer.parseInt(o1.split("\\.")[0]);
                int split2 = Integer.parseInt(o2.split("\\.")[0]);
                return split1 - split2;
            }
        });
        System.out.println(list);

        BufferedWriter bw = new BufferedWriter(new FileWriter("chapter18\\newcsb.txt"));
        for (String s : list) {
            bw.write(s);
            bw.newLine();
        }
        bw.close();
    }
}
(2)方式二:使用TreeMap
public class TestDemo6 {
    public static void main(String[] args) throws IOException {
        // 使用TreeMap自动排序
        BufferedReader br = new BufferedReader(new FileReader("chapter18\\csb.txt"));
        TreeMap<Integer, String> treeMap = new TreeMap<>();
        String len;
        while ((len = br.readLine()) != null) {
            String[] split = len.split("\\.");
            treeMap.put(Integer.parseInt(split[0]), split[1]);
        }

        br.close();
        System.out.println(treeMap);

        BufferedWriter bw = new BufferedWriter(new FileWriter("chapter18\\newcsb1.txt"));
        Set<Map.Entry<Integer, String>> entries = treeMap.entrySet();
        for (Map.Entry<Integer, String> entry : entries) {
            Integer key = entry.getKey();
            String value = entry.getValue();
            String str = key + "." + value;
            bw.write(str);
            bw.newLine();
        }
        bw.close();
    }
}

3.控制软件运行次数

实现一个验证程序运行次数的小程序,要求如下:
        1.当程序运行超过3次时给出提示:本软件只能免费使用3次,欢迎您注册会员后继续使用~
        2.程序运行演示如下:
                第一次运行控制台输出: 欢迎使用本软件,第1次使用免费~
                第二次运行控制台输出: 欢迎使用本软件,第2次使用免费~
                第三次运行控制台输出: 欢迎使用本软件,第3次使用免费~
                第四次及之后运行控制台输出:本软件只能免费使用3次,欢迎您注册会员后继续使用~

public class TestDemo7 {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("chapter18\\count.txt"));
        // 读取
        String line = br.readLine();
        br.close();

        int count = Integer.parseInt(line);
        count++;

        if (count <= 3) {
            System.out.println("欢迎使用本软件,第" + count + "次使用免费~");
        } else {
            System.out.println("本软件只能免费使用3次,欢迎您注册会员后继续使用~");
        }
        // 写出
        BufferedWriter bw = new BufferedWriter(new FileWriter("chapter18\\count.txt"));
        bw.write(count + "");
        bw.close();
    }
}

4.统计项目文件中,.java文件的总行数

public class TestDemo {
    public static void main(String[] args) throws IOException {
        // 统计文件中代码行数,过滤.java文件
        File file = new File("G:\\develop\\workspace");
        // 方式一:
        getJavaCount(file);
        System.out.println(javaLine); // 29275

        // 方式二:
        count(file);
        System.out.println(count);
    }

    static int javaLine = 0;

    private static void getJavaCount(File file) throws IOException {
        BufferedReader bis;
        File[] files = file.listFiles();
        for (File f : files) {
            if (f.isFile()) {
                if (f.getName().endsWith(".java")) {
                    // 读取文件
                    bis = new BufferedReader(new FileReader(f));
                    while (bis.readLine() != null) {
                        javaLine++;
                    }
                    bis.close();
                }
            } else {
                getJavaCount(f);
            }
        }
    }

    static int count = 0;

    private static void count(File file) throws IOException {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                count(f);
            }
        } else if (file.getName().endsWith(".java")) {
            BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
            while (bufferedReader.readLine() != null) {
                count++;
            }
        }
    }
}

四、IO流使用小贴士

IO流随用随创建,什么时候不用什么时候关闭。

如果是对同一个文件进行操作,同时开启输入输出流,文件会被清空,只会读取缓冲区中的数据。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值