>>分享Java编程技术,对《Java面向对象编程》等书籍提供技术支持 书籍支持  卫琴直播  品书摘要  在线测试  资源下载  联系我们
发表一个新主题 开启一个新投票 回复文章 您是本文章第 29490 个阅读者 刷新本主题
 * 贴子主题:  《Java面向对象编程》P560的表述问题 回复文章 点赞(0)  收藏  
作者:apolo    发表时间:2019-07-11 20:33:52     消息  查看  搜索  好友  邮件  复制  引用

    在书P560介绍BufferedOutpuStream/BufferedOutputStream这个缓冲输出流/输出流时,文中写道:
“由于字符串“你好啊”的UTF-8的编码超过了缓冲区的容量,BufferedOutputStream会自动把缓冲区的所有数据写道data.txt文件中”,这里书中设定了一个条件就是将缓冲区大小设置为2个字节。那么如果我们写入的字符串的字节数为奇数个子节,那么最终多出来的字节数会不会写入到文件中呢?由于缓冲区固定大小为2,那么剩下的字节数该如何处理呢?
    假定缓冲区的大小为size,BufferedOutputStream是一旦缓冲区满,那么就直接将缓冲区中的内容写入到文件或数据汇中,然后继续从字符串的字节数组中取size个字节,然后继续判断,那么最终会剩下1个字节不会写入到文件中。基于上述假设,我将要写入的字符串设定为“abc”,那么字节数为3,最终运行结果发现仍然能够将“abc”所有的内容写入到文件中,而不是“ab”。因此Java并不是按照上述设定来进行缓冲区数据的读写的。
    于是我阅读了BufferedOutputStream的源码,其中writeUTF()中调用了BufferedOutput的write方法。这个方法大致如下:
static int writeUTF(String str, DataOutput out) throws IOException {
        int strlen = str.length();
        int utflen = 0;
        int c, count = 0;

        /* use charAt instead of copying String to char array */
        for (int i = 0; i < strlen; i++) {
            c = str.charAt(i);
            if ((c >= 0x0001) && (c <= 0x007F)) {
                utflen++;
            } else if (c > 0x07FF) {
                utflen += 3;
            } else {
                utflen += 2;
            }
        }

        if (utflen > 65535)
            throw new UTFDataFormatException(
                "encoded string too long: " + utflen + " bytes");
       //上述代码是统计字符串的字节个数
        byte[] bytearr = null;
        if (out instanceof DataOutputStream) {
            DataOutputStream dos = (DataOutputStream)out;
            if(dos.bytearr == null || (dos.bytearr.length < (utflen+2)))
                dos.bytearr = new byte[(utflen*2) + 2];
            bytearr = dos.bytearr;
        } else {
            bytearr = new byte[utflen+2];
        }

        bytearr[count++] = (byte) ((utflen >>> 8) & 0xFF);
        bytearr[count++] = (byte) ((utflen >>> 0) & 0xFF);

        int i=0;
        for (i=0; i<strlen; i++) {
           c = str.charAt(i);
           if (!((c >= 0x0001) && (c <= 0x007F))) break;
           bytearr[count++] = (byte) c;
        }
        //上述代码是将字符串转化为字节数组,然后存放在byrearr中
        //中间代码略
        out.write(bytearr, 0, utflen+2);
        return utflen + 2;
        //调用BufferedOutputStream对象的write(byte b[], int off, int len)方法
      }
BufferedOutputStream方法为:
   public synchronized void write(byte b[], int off, int len) throws IOException {
        if (len >= buf.length) {
            flushBuffer();
            out.write(b, off, len);
            return;
        }
        if (len > buf.length - count) {
            flushBuffer();
        }
        System.arraycopy(b, off, buf, count, len);
        count += len;
    }
字节数组b为要写入的字符串的字节数组,buf为缓冲区字节数组,从上述代码可以看出,BufferedOutputStream首先会判断所要写入的内容字节数是否大于缓冲区容量size
如果大于缓冲区容量size:
       先将缓冲区已有的内容写入到文件或数据汇中
       然后将字符串的字节数组直接写入到文件中(不经过缓冲区)

如果小于缓冲区大小:
        判断字符串的字节大小加上缓冲区已有的字节长度是否大于缓冲区容量size
        如果大于缓冲区容量size
             先将缓冲区内容写入到文件中
将字符串字节数组b直接复制到缓冲区数组b中

因此,BufferedOutputStream的写入规则是:
如果写入的字符串的字节数大于缓冲区:
      那么首先将缓冲区的内容先写入到文件中,然后直接将该字符串的字节数组b写入到文件中
否则,判断字符串字节数组b的大小加上缓冲区中已存在的字节数之和是否大于缓冲区容量size,如果大于缓冲区容量,就将缓冲区的内容写入到文件中,然后再将字符串字节数组写入到缓冲区数组中,如果小于缓冲区容量size, 那么直接将字符串字节数组复制到缓冲区中。


程序猿的技术大观园:www.javathinker.net
  Java面向对象编程-->多线程(下)
  JavaWeb开发-->使用Session(Ⅱ)
  JSP与Hibernate开发-->JPA API的高级用法
  Java网络编程-->XML数据处理
  精通Spring-->计算属性和数据监听
  Vue3开发-->通过Vuex进行状态管理
  Java虚拟机安全性-class文件检验器
  java实现动态编译并动态加载
  套接字通信:select、poll、epoll的区别
  BIO模型的缺陷
  18 张图弄懂面试官必问的一致性哈希
  不修改源代码,动态注入Java代码的方法
  Redis安装、Redis基本数据类型、Jedis、Redis集群搭建
  被迫重构代码,这次我干掉了 if-else
  Java方法的嵌套与递归调用
  Eclipse使用指南:工作空间(Workspace)
  Java设计模式:解释器模式
  Socket服务器的整体架构
  Java入门实用代码:获取所有线程
  Java 入门实用代码:设置文件只读
  Java入门实用代码:文件重命名
  更多...
 IPIP: 已设置保密
楼主      
该用户目前不在线 sunweiqin 
  

威望: 0
级别: 高级天王 [荣誉]
魅力: 480
经验: 480
现金: 1128
发文章数: 171
注册时间: 2016-09-12
 消息  查看  搜索  好友  邮件  复制  引用


To apolo,你好。

非常赞叹你用认真专研的精神去阅读本书,深入探寻Java类的运作细节。这样的学习方式一定会让你成为精通Java的高手,编写出安全、健壮、高效的Java程序。

在你的点拨下,我也阅读了BufferedOutputStream的源码,你所总结的运行规则完全正确。由此可以看出,应该为缓冲区设定一个合理的容量大小。就像从上海到北京运送货物,到底用多大容量的卡车来运输,需要进行权衡。如果卡车容量太小,就不划算,在装货和卸货上会浪费很多时间,还不如一次性把货物直接运到目的地。

发文章时间 2019-07-18 20:55:40
 IPIP: 已设置保密 1 楼     
1页 1条记录 当前第1
发表一个新主题 开启一个新投票 回复文章


中文版权所有: JavaThinker技术网站 Copyright 2016-2026 沪ICP备16029593号-2
荟萃Java程序员智慧的结晶,分享交流Java前沿技术。  联系我们
如有技术文章涉及侵权,请与本站管理员联系。