JAVA基础知识之BufferedWriter流

JAVA基础知识之BufferedWriter流一、BufferedWriter流    API文档说明:  1)将文本写入字符输出流,缓冲字符,以便有效地写入单个字符,数组和字符串?   说明存在用单个字符、数组、字符串作为参数的方法写入数据    2)可以指定缓冲区大小,或者可以接受默认大小。对于大多数用途,默认值足够大?   说明该类存在一个常量值用作默认缓冲区大小同时也可以通过构造函数指定大小    3)…

大家好,又见面了,我是你们的朋友全栈君。

一、BufferedWriter流

     API文档说明:

   1)将文本写入字符输出流,缓冲字符,以便有效地写入单个字符,数组和字符串?

    说明存在用单个字符、数组、字符串作为参数的方法写入数据

     2)可以指定缓冲区大小,或者可以接受默认大小。对于大多数用途,默认值足够大?

    说明该类存在一个常量值用作默认缓冲区大小同时也可以通过构造函数指定大小

     3)提供了一个newLine()方法,它使用平台自己的行分隔符概念,由系统属性line.separator定义。并非所有平台都使用换行符(’\ n’)来终止行。因此,调用此方法终止每个输出行比直接编写换行符更为可取。

       用于进行输出的时候做换行操作且自动适用平台的换行分隔符,而非自定义的,比较灵活

     4) 通常,Writer会立即将其输出发送到基础字符或字节流。除非需要提示输出,否则建议将BufferedWriter包装在任何write()操作可能代价高昂的Writer周围,例如FileWriters和OutputStreamWriters。

      意味着可以用BufferedWriter来包装write的子类提高输出效率
    

二、BufferedWriter流实例域

      可以看出存在一个默认字符缓冲区,且默认大小是8192个字符

 //字符输出流
    private Writer out;

    //字符缓冲区
    private char cb[];
    
    //设置的字符缓冲区大小变量
    private int nChars;
    
    //字符缓冲区中的已存储元素的位置
    private int  nextChar;

    //默认字符缓冲区大小
    private static int defaultCharBufferSize = 8192;

    /**
     * 行分割字符串-
     * property at the moment that the stream was created.
     */
    private String lineSeparator;
    

三、BufferedWriter流构造函数

      1、参数Writer out实际是Writer的子类如FileWriters和OutputStreamWriters等

      2、 参数sz就是用于指定字符缓冲区大小的变量


    /**
     * 
     * 默认字符缓冲区大小的构造函数
     *
     * @param  out  A Writer
     */
    public BufferedWriter(Writer out) {
        this(out, defaultCharBufferSize);
    }

    /**
     * 指定字符缓冲区大小的构造函数
     */
    public BufferedWriter(Writer out, int sz) {
        super(out);
        if (sz <= 0)
            throw new IllegalArgumentException("Buffer size <= 0");
        this.out = out;
        cb = new char[sz];
        nChars = sz;
        nextChar = 0;

        lineSeparator = java.security.AccessController.doPrivileged(
           new sun.security.action.GetPropertyAction("line.separator"));
    }

四、BufferedWriter流的API

1)写一个字符到字符缓冲区中:本质是写入字符到字符缓冲区中

 /**
     * 写入一个字符,并存入到字符缓冲区中
     *
     * @exception  IOException  If an I/O error occurs
     */
    public void write(int c) throws IOException {
        synchronized (lock) {
            ensureOpen();
            if (nextChar >= nChars)
                flushBuffer();
            cb[nextChar++] = (char) c;  //将单个字符存入到
        }
    }

2)写一个字符数组的一部分:本质是调用OutputStreamWriter的write(cbuf, off, len)方法而OutputStreamWriter的write(cbuf, off, len)方法实际是调用StreamEncoder的write方法执行的

     1、传入字符数组cbuf,字符数组的偏移点off,以及要写入的字符个数len–代表要从字符数组cbuf中下标off开始写入len个字符

     2、 前期条件判断避免出现RuntimeException异常所以必须抛出

     3、 当要写入的字符个数len大于字符缓冲区的长度时则意味着就算字符缓冲区cb是空的也无法载入写入字符,那么就没必有调用字符缓冲区了,直接调用write(cbuf, off, len)方法,out变量是OutputStreamWriter的对象,而OutputStreamWriter内的方法都是StreamEncoder类的方法执行。因此实际调用的是StreamEncoder的write方法。在StreamEncoder中直接把字符数组cbuf通过编码器编码到StreamEncoder的字节缓冲区中

    4、当要写入的字符个数len小于字符缓冲区的长度时,通过 System.arraycopy的方法把字符数组cbuf内要写入的字符复制到字符缓冲区cb中,其中while循环的作用就是保证符数组cbuf内要写入的字符全部复制到字符缓冲区cb中

  * 写一个字符数组的一部分
    *
     */
    public void write(char cbuf[], int off, int len) throws IOException {
        synchronized (lock) {
            ensureOpen();
            if ((off < 0) || (off > cbuf.length) || (len < 0) ||
                ((off + len) > cbuf.length) || ((off + len) < 0)) {
                throw new IndexOutOfBoundsException();
            } else if (len == 0) {
                return;
            }

            if (len >= nChars) {
                //如果请求长度超过输出缓冲区的大小则直接进行输出,而不用字符缓冲区做缓冲
                flushBuffer();
                out.write(cbuf, off, len);  //实际是调用的OutputStreamWriter的write方法本质是调用StreamEncoder的write方法执行的
                return;
            }

            int b = off, t = off + len;
             
             //此循环的目的就是保证len个字符务必被复制到字符缓冲区中区
                while (b < t) {      
                int d = min(nChars - nextChar, t - b);  //判断字符缓冲区中的剩余位置和要写入的字符长度最小值,若字符缓冲区空间充足,则复制len个字符,若不充足,则复制剩余位置个字符
                System.arraycopy(cbuf, b, cb, nextChar, d); //从目标数组cbuf的下标b中复制d个字符到字符缓冲区
                b += d;
                nextChar += d;
                if (nextChar >= nChars) //判断字符缓冲区是否已满?若满了,则调用flushBuffer方法把缓冲区中的内容全部输出,然后按道理此时字符缓冲区应该就是空了的?
                    flushBuffer();
            }
        }
    }

3)写字符串的一部分:本质跟第二个写字符数组的一部分一致

/**
     * 写一个字符串的一部分
     *  若len为负值,则没有任何字符被写入,
     */
    public void write(String s, int off, int len) throws IOException {
        synchronized (lock) {
            ensureOpen();
            int b = off, t = off + len;
            while (b < t) {
                int d = min(nChars - nextChar, t - b);
                s.getChars(b, b + d, cb, nextChar); //本质跟System.arraycopy(cbuf, b, cb, nextChar, d);一样,将内容复制 到缓冲区中
                b += d;
                nextChar += d;
                if (nextChar >= nChars)
                    flushBuffer();
            }
        }
    }

4)close方法–关闭流资源,关闭之前会先调用flushBuffer()方法然后调用StreamEncoder的write方法把字符缓冲区的内容写入到StreamEncoder的字节缓冲区中,最后调用close方法在把StreamEncoder的字节缓冲区内容输出到计算机中

 /**
     * 关闭流,关闭之前先刷新缓冲区,然后再关闭流
     */

    public void close() throws IOException {
        synchronized (lock) {
            if (out == null) {
                return;
            }
            try {
                flushBuffer();
            } finally {
                out.close();  //实际调用的是OutputStreamWriter的close方法
                out = null;
                cb = null;
            }
        }
    }
    
    /**
     * 将字符缓冲区中的内容写入到字节缓冲区中
     */
    void flushBuffer() throws IOException {
        synchronized (lock) {
            ensureOpen();
            if (nextChar == 0)
                return;
            out.write(cb, 0, nextChar);   //注意这里的方法调用到了实现类outputSteam类的write方法
            nextChar = 0;
        }
    }

5)flush方法:刷新缓冲区数据,其本质是先调用flushBuffer方法把BufferedWriter类中的字符缓冲区内容写入到StreamEncoder的字节缓冲区,而后调用OutputStreamWriter的flush方法把StreamEncoder的字节缓冲区内容给写出去,因此和close方法作用一致,但是没有关闭资源链接达到释放资源作用,所以项目中一般是先进行flush工作保证字节输出,而后调用close做二次保证以及关闭资源

    /**
     * 刷新缓冲区将缓冲区字符编码到字节缓冲区中
     *
     * @exception  IOException  If an I/O error occurs
     */
    public void flush() throws IOException {
        synchronized (lock) {
            flushBuffer();
            out.flush();
        }
    }

五、BufferedWriter流与OutputStreamWriter的关系

     1)前面OutputStreamWriter的API说明中提到过:为了获得最高效率,请考虑在BufferedWriter中包装OutputStreamWriter,以避免频繁的转换器调用这句话,理解如下:

         当我们直接使用OutputStreamWriter进行输出时,不管是一个字符,还是一个数组,都会调用字符编码器进行转换,因为其是直接调用的StreamEncoder的implWrite方法,若存在for循环100次,每次调用write(int c)方法写入一个字符,那么最终就会调用100次的implWrite方法,就会调用100次的字符编码器进行转换

       但是当我们使用BufferedWriter包装OutputStreamWriter时,每次调用write(int c)方法时,都只是将字符写入到BufferedWriter类的字符缓冲区中。到最后调用flush方法或者close方法时,才会调用一次StreamEncoder的implWrite方法,也就是调用一次字符编码器

      因此BufferedWriter可以提高字符输出的效率,提高的关键点在于:存在字符缓冲区用于存储字符

 

2)因此根据对FileWriter、OutputStreamWriter、StreamEncoder、BufferedWriter的源码分析,我们可以得出以下几点

     2.1    以上4个类的起到字符编码成字节并调用底层字节输出流的是StreamEncoder类

    2.2     FileWriter类和OutputStreamWriter类只是傀儡性质,实际并未发挥作用

    2.3     项目中遇到文件字符输出流时,请使用BufferedWriter包装OutputStreamWriter类,以便提高效率

    2.4    StreamEncoder类在jdk1.7的版本中是没有源码可以导入的,因此不建议直接使用

    2.5    若要理解透彻,可以对以上4个类源码进行重写,然后做实际数据验证,就可以理解每一个方法的作用了

   

    

 

 

 

     

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/132709.html原文链接:https://javaforall.net

(0)
上一篇 2022年6月10日 下午1:00
下一篇 2022年6月10日 下午1:00


相关推荐

  • mysql实现主从复制的日志是哪种_Redis主从复制

    mysql实现主从复制的日志是哪种_Redis主从复制Mysql实现主从复制

    2022年8月13日
    8
  • 整除的尾数_整除数

    整除的尾数_整除数整除的尾数时间限制:1000 ms | 内存限制:65535 KB难度:0描述一个整数,只知道前几位,不知道末二位,被另一个整数除尽了,那么该数的末二位该是什么呢?输入输入数据有若干组,每组数据包含二个整数a,b(0输出对应每组数据,将满足条件的所有尾数在一行内输出,格式见样本输出。同组数据的输出,其每个尾数之间空一格,行末没有空格。样例输入

    2025年5月24日
    3
  • BIND9配置文件详解

    BIND9配置文件详解参考 ISCBIND9 最详细 最认真的从零开始的 BIND9 DNS 服务搭建及其原理讲解一 conf 文件中字段详解 1 acl 一般来说 ACL 模块用来承担控制主机可以访问域名解析服务器的角色 其设置不会让控制文件的配置非常冗余和庞大 采用这个配置可以有效防范 DOS 以及 Spoofing 攻击 一般来说定义这部分的内容来规定 IP 是否能够被接入以及 Blacklist 来阻止某些特定的 IP 地址介入到域名解析服务器中 ACL 匹配客户端是否能够接入到域名服务器基于三个基本的特征 客户端的 IPv4

    2026年3月26日
    2
  • Android学习路线指南

    Android学习路线指南前言看到一篇文章中提到“最近几年国内的初级Android程序员已经很多了,但是中高级的Android技术人才仍然稀缺“,这的确不假,从我在百度所进行的一些面试来看,找一个适合的高级Android工程师的确不容易,一般需要进行大量的面试才能挑选出一个比较满意的。为什么中高级Android程序员不多呢?这是一个问题,我不好回答,但是我想写一篇文章来描述下Android的学习路线,期望可以帮助更多的…

    2022年6月11日
    38
  • java用户态和内核态「建议收藏」

    java用户态和内核态「建议收藏」在&lt;深入理解java虚拟机&gt;这本书上多次看到用户态和内核态两个名词,虽然大概能明白意思.但对于两者具体的定义和区别还是比较,特此查阅之后记录.内核态(KernelMode)与用户态(UserMode)内核态:CPU可以访问内存所有数据,包括外围设备,例如硬盘,网卡.CPU也可以将自己从一个程序切换到另一个程序用户态:只能受限的访问内存,且不允许访问外…

    2026年1月14日
    6
  • 至孝孝其慧_惟孝

    至孝孝其慧_惟孝20060222: 孝?

    2022年4月22日
    35

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号