java IO流(二) 字符流 缓冲流 原始流与缓冲流性能分析

请添加图片描述

字符流

前面学习的字节流虽然可以读取文件中的字节数据,但是如果文件中有中文,使用字节流来读取,就有可能读到半个汉字的情况,这样会导致乱码。虽然使用读取全部字节的方法不会出现乱码,但是如果文件过大又不太合适。
所以Java专门提供了另外一种流,叫字符流,字符流是专门为读取文本数据而生的。

FileReader类 读取字符
FileReader读取文件的步骤如下:

第一步:创建FileReader对象与要读取的源文件接通
第二步:调用read()方法读取文件中的字符
第三步:调用close()方法关闭流

请添加图片描述

public class FileReaderTest1 {
    public static void main(String[] args)  {
        try (
                // 1、创建一个文件字符输入流管道与源文件接通
                Reader fr = new FileReader("io-app2\\src\\01.txt");
                ){
            // 2、一个字符一个字符的读(性能较差)
//            int c; // 记住每次读取的字符编号。
//            while ((c = fr.read()) != -1){
//                System.out.print((char) c);
//            }
            // 每次读取一个字符的形式,性能肯定是比较差的。

            // 3、每次读取多个字符。(性能是比较不错的!)
            char[] buffer = new char[3];
            int len; // 记住每次读取了多少个字符。
            while ((len = fr.read(buffer)) != -1){
                // 读取多少倒出多少
                System.out.print(new String(buffer, 0, len));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

FileWriter类 写字符
FileWriter往文件中写字符数据的步骤如下:

第一步:创建FileWirter对象与要读取的目标文件接通
第二步:调用write(字符数据/字符数组/字符串)方法读取文件中的字符
第三步:调用close()方法关闭流

请添加图片描述

public class FileWriterTest2 {
    public static void main(String[] args) {
        try (
                // 0、创建一个文件字符输出流管道与目标文件接通。
                // 覆盖管道
                // Writer fw = new FileWriter("io-app2/src/02out.txt");
                // 追加数据的管道
                Writer fw = new FileWriter("io-app2/src/02out.txt", true);
                ){
            // 1、public void write(int c):写一个字符出去
            fw.write('a');
            fw.write(97);//写了个a
            //fw.write('磊'); // 写一个字符出去
            fw.write("\r\n"); // 换行符 win平台是\n 但是\r\n能兼容更多平台

            // 2、public void write(String c)写一个字符串出去
            fw.write("我爱你中国abc");
            fw.write("\r\n");

            // 3、public void write(String c ,int pos ,int len):写字符串的一部分出去
            fw.write("我爱你中国abc", 0, 5);
            fw.write("\r\n");

            // 4、public void write(char[] buffer):写一个字符数组出去
            char[] buffer = {'黑', '马', 'a', 'b', 'c'};
            fw.write(buffer);
            fw.write("\r\n");

            // 5、public void write(char[] buffer ,int pos ,int len):写字符数组的一部分出去
            fw.write(buffer, 0, 2);
            fw.write("\r\n");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这里有一个小问题,FileWriter写完数据之后,必须刷新或者关闭,写出去的数据才能生效。
比如:下面的代码只调用了写数据的方法,没有关流的方法。当你打开目标文件时,是看不到任何数据的。

//1.创建FileWriter对象
Writer fw = new FileWriter("io-app2/src/03out.txt");

//2.写字符数据出去
fw.write('a');
fw.write('b');
fw.write('c');

而下面的代码,加上了flush()方法之后,数据就会立即到目标文件中去。

//1.创建FileWriter对象
Writer fw = new FileWriter("io-app2/src/03out.txt");

//2.写字符数据出去
fw.write('a');
fw.write('b');
fw.write('c');

//3.刷新
fw.flush(); 

下面的代码,调用了close()方法,数据也会立即到文件中去。因为close()方法在关闭流之前,会将内存中缓存的数据先刷新到文件,再关流。

//1.创建FileWriter对象
Writer fw = new FileWriter("io-app2/src/03out.txt");

//2.写字符数据出去
fw.write('a');
fw.write('b');
fw.write('c');

//3.关闭流
fw.close(); //会先刷新,再关流

但是需要注意的是,关闭流之后,就不能在对流进行操作了。否则会出异常


缓冲流

请添加图片描述

缓冲流有四种,如上图所示.缓冲流的作用:可以对原始流进行包装,提高原始流读写数据的性能。

字节缓冲流
其是在缓冲流的底层自己封装了一个长度为8KB(8129byte)的字节数组,但是缓冲流不能单独使用,它需要依赖于原始流。
读数据时它先用原始字节输入流一次性读取8KB的数据存入缓冲流内部的数组中,再从8KB的字节数组中读取一个字节或者多个字节。
写数据时 它是先把数据写到缓冲流内部的8BK的数组中,等数组存满了,再通过原始的字节输出流,一次性写到目标文件中去。

请添加图片描述

请添加图片描述

在创建缓冲字节流对象时,需要封装一个原始流对象进来。构造方法如下

请添加图片描述

PS构造函数第二个参数可以指定底层封装的字节数组大小,默认为8k。

如果我们用缓冲流复制文件,代码写法如下:

public class BufferedInputStreamTest1 {
    public static void main(String[] args) {
        try (
                InputStream is = new FileInputStream("io-app2/src/01.txt");
                // 1、定义一个字节缓冲输入流包装原始的字节输入流
                InputStream bis = new BufferedInputStream(is);

                OutputStream os = new FileOutputStream("io-app2/src/01_bak.txt");
                // 2、定义一个字节缓冲输出流包装原始的字节输出流
                OutputStream bos = new BufferedOutputStream(os);
        ){

            byte[] buffer = new byte[1024];
            int len;
            while ((len = bis.read(buffer)) != -1){
                bos.write(buffer, 0, len);
            }
            System.out.println("复制完成!!");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

字符缓冲流
它的原理和字节缓冲流是类似的,它底层也会有一个8KB的数组,但是这里是字符数组。字符缓冲流也不能单独使用,它需要依赖于原始字符流一起使用。构造方法如下

请添加图片描述

请添加图片描述

BufferedReader还有新增的方法,一次可以读取文本文件中的一行:

请添加图片描述

BufferedWriter还有新增的方法,可以用来写一个换行符:

请添加图片描述

使用BufferedReader读取数据的代码如下

public class BufferedReaderTest2 {
    public static void main(String[] args)  {
        try (
                Reader fr = new FileReader("io-app2\\src\\04.txt");
                // 创建一个字符缓冲输入流包装原始的字符输入流
                BufferedReader br = new BufferedReader(fr);
        ){
//            char[] buffer = new char[3];
//            int len;
//            while ((len = br.read(buffer)) != -1){
//                System.out.print(new String(buffer, 0, len));
//            }
//            System.out.println(br.readLine());
//            System.out.println(br.readLine());
//            System.out.println(br.readLine());
//            System.out.println(br.readLine());

            String line; // 记住每次读取的一行数据
            while ((line = br.readLine()) != null){
                System.out.println(line);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

使用BufferedWriter往文件中写入字符数据

public class BufferedWriterTest3 {
    public static void main(String[] args) {
        try (
                Writer fw = new FileWriter("io-app2/src/05out.txt", true);
                // 创建一个字符缓冲输出流管道包装原始的字符输出流
                BufferedWriter bw = new BufferedWriter(fw);
        ){

            bw.write('a');
            bw.write(97);
            bw.write('磊');
            bw.newLine();

            bw.write("我爱你中国abc");
            bw.newLine();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

缓冲流性能分析
我们说缓冲流内部多了一个数组,可以提高原始流的读写性能。它和我们使用原始流,自己加一个8BK数组不是一样的吗? 缓冲流就一定能提高性能吗?结论:缓冲流不一定能提高性能。缓冲流的性能不一定比低级流高,其实低级流自己加一个数组,性能其实是不差,只不过缓冲流帮你加了一个相对而言大小比较合理的数组 。

下面我们用一个比较大的文件(889MB)进行复制,做性能测试,分别使用下面四种方式来完成文件复制,并记录文件复制的时间。


1使用低级流一个字节一个字节的复制

2使用低级流按照字节数组的形式复制

3使用缓冲流一个字节一个字节的复制

4使用缓冲流按照字节数组的形式复制

低级流一个字节复制: 慢得简直让人无法忍受
低级流按照字节数组复制(数组长度1024): 12.117s
缓冲流一个字节复制: 11.058s
缓冲流按照字节数组复制(数组长度1024): 2.163s

经过上面的测试,我们可以得出一个结论:默认情况下,采用一次复制1024个字节,缓冲流完胜。
但是,缓冲流就一定性能高吗?我们采用一次复制8192个字节试试

低级流按照字节数组复制(数组长度8192): 2.535s
缓冲流按照字节数组复制(数组长度8192): 2.088s

经过上面的测试,我们可以得出一个结论:一次读取8192个字节时,低级流和缓冲流性能相当。相差的那几毫秒可以忽略不计。
继续把数组变大,看一看缓冲流就一定性能高吗?现在采用一次读取1024*32个字节数据试试(缓冲流底层的字节数组也设成32k)

低级流按照字节数组复制(数组长度32k): 1.128s
缓冲流按照字节数组复制(数组长度32k): 1.133s

继续把数组变大,看一看缓冲流就一定性能高吗?现在采用一次读取1024*46个字节数据试试

低级流按照字节数组复制(数组长度64k): 1.039s
缓冲流按照字节数组复制(数组长度64l): 1.151s

此时你会发现,当数组大到一定程度,性能已经提高不了多少了,甚至缓冲流的性能还没有低级流高。
在实际开发中,想提升读写性能就扩大数组大小,大小取决于经验,并且缓冲流的性能不一定就比低级流好。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值