A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 RedProtector 于 2015-8-20 13:42 编辑

我们都知道java中io操作分为字节流和字符流,对于字节流,顾名思义是按字节的方式读取数据,所以我们常用字节流来读取二进制流(如图片,音乐 等文件)。问题是为什么字节流中定义的read()方法返回值为int类型呢?既然它一次读出一个字节数据为什么不返回byte类型呢?
   下面先把java的源码贴出来(以BufferedInputStream/BufferedOutputStream为例):
[java] view plaincopy
//BufferedInputStream中的read()方法的实现  
[java] view plaincopy
   public synchronized int read() throws IOException {  
       if (pos >= count) {  
           fill();  
       if (pos >= count)   
           return -1;  
       }  
       return getBufIfOpen()[pos++] & 0xff;//这边getBufIfOpen()返回的是byte[],而& 0xff是为了保证由char类型向上拓展成int的时候,不进行符号拓展,而是0拓展。  
       从源码中我们得到的信息是直到getBufIfOpen()方法返回,我们得到的都是byte类型,可是为什么方法的最终返回值是int?
        首先解释下符号扩展,这是指由byte向上转化成更宽的类型时,是扩展的符号位。这对于正数补0,负数补1,例如,定义byte b = -1;在计算机内部它是用八位1111 1111表示的,当扩展成32位整型的时候,一般情况下是1111 1111 1111 1111 1111 1111 1111 1111,即符号扩展,而对于无符号扩展,也称为0扩展,其结果是0000 0000 0000 0000 0000 0000 1111 1111(实际上这样一来值已经变成255了)。这里要说的是我们能从java源码中得到的第二 个信息,即上面的注释部分,read()方法的最后一行把读到的字节0扩展成了int,也就是说如果我们直接读出来这个值可能就是不对了。为什么BufferedOutputStream中的writer()方法能正确读出字节呢?查下对应的源码:
[java] view plaincopy
public synchronized void write(int b) throws IOException {  
        if (count >= buf.length) {  
            flushBuffer();  
        }  
        buf[count++] = (byte)b;  
    }  
可以看到,这里它果断的又将int强制转成了byte(截取后八位)。于是总结下现在得到的信息是,java字节流把byte转成int读出来再转回byte存起来。何必呢?
经过一番思考,我初步有了答案:在用输入流读取一个byte数据时,有时会出现连续8个1的情况,这个值在计算机内部表示-1,正 好符合了流结束标记。所以为了避免流操作数据提前结束,将读到的字节进行int类型的扩展。保留该字节数据的同时,前面都补0,避免出现-1的情况。而真 正读到文件最后结束是通过这句实现的:
[java] view plaincopy
if (pos >= count)  return -1;  
所以我们使用的-1这个结束标志是通过这句返回的,而不是输入流读到了一个-1


3 个回复

正序浏览
风华正茂 来自手机 中级黑马 2015-8-20 12:31:41
板凳
谢谢楼主分享,楼主辛苦了
回复 使用道具 举报
BufferedInputStream也能设置一个缓冲数组
回复 使用道具 举报
这里我也是蛮模糊的,知道大概有这么一回事。
那我想问一下,我是这样理解的:读到了一个字节,然后就把他扩展成Int,再把它作为返回值返回。总感觉这样会很慢多做了很多事。
而且,最让我不能理解的是BufferedInputStream。你看没加Buffered的InputStream我还能设置一个[1024]的缓冲数组,怎么到Buffered这里了它反而还成了一个字节一个字节的读了?你看BufferedReader的缓冲也是一个字符串啊。
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马