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

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 一步一脚印 中级黑马   /  2013-11-15 03:03  /  1240 人查看  /  1 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文


图片上显示的是InputStream基类中的read()方法的api说明,不知道大家有没有发现,read()方法的返回类型是int.
而InputStream是字节输入流对象,也就是它的子类读取文件是一个字节一个字节的读。
那就出现了一个问题,为什么read()方法不返回byte类型而要返回int类型呢?
看一段我们自己自定义的read()方法。
  1. public int myRead() throws IOException{
  2.                        
  3.                         if(count == 0){
  4.       count = in.read(buf);
  5.        if(count<0)
  6.              return -1;//即输入流读取完数据返回值为-1
  7.                        
  8.       pos = 0;
  9.      byte b =buf[pos];
  10.      count--;//每次读取是一个字节一个字节的读,读一次--一次
  11.      pos++;
  12.      return b;
  13.                         }else if(count>0){
  14.                                
  15.      byte b =buf[pos];
  16.      count--;//每次读取是一个字节一个字节的读,读一次--一次
  17.      pos++;
  18.      return b;
  19.                         }
  20.     return -1;
  21.         }
复制代码
我们读取文件的代码
  1. while((len=fis.read())!=-1){
  2.      //进行写操作
  3.                  }
复制代码
可以看出,当我们读取到-1的时候,终止程序。
问题就是出在这里,字节流的读取是按一个个字节读取的
拿mp3举例,mp3的文件是有很多的2进制数组成。比如
11111111111111101010010100101001010000000000011111111111................
这样的数字组成。那么是不是存在一种情况,当读取的第一个字节就为-1。

我们分析一下,
0000 0001  是1 , -1的二进制是1的二进制取反+1即
1111 1110 +1
------------
1111 1111

所以当出现前8位2进制全为1时, 字节流读取的值就为-1,将会终止写入文件。这就是一种非正常的行为。

为了避免-1这种情况和读取文件结束的-1冲突。
为什么myRead()返回值为int,就是为了保证转型时byte的高位补0,而不是补1.同时不改变原来的值
分析一下
byte:-1  (8)位  ---->  int : -1; (32)位
11111111          11111111 11111111 11111111 11111111


例如,将字节 11111111 -->提升了一个int类型, 是-1的原因是因为在8个1面前补的是1导致的,
那么我只要在前面补0,即可以保留原字节数据不变,又可以避免-1的出现。
那么怎么补0呢?


可以采用
11111111 11111111 11111111 11111111
00000000 00000000 00000000 11111111
----------------------------------- &
00000000 00000000 00000000 11111111
就满足了需要的条件

所以将代码改进之后为
  1. public int myRead() throws IOException{
  2.                        
  3.                         if(count == 0){
  4.                                 count = in.read(buf);
  5.                                 if(count<0)
  6.                                         return -1;//即输入流读取完数据返回值为-1
  7.                        
  8.                                 pos = 0;
  9.                                 byte b =buf[pos];
  10.                                 count--;//每次读取是一个字节一个字节的读,读一次--一次
  11.                                 pos++;
  12.                                 return <font color="#ff0000">b&255;</font>//这里改进,解决了2次读取时出现2进制为-1的情况。
  13.                         }else if(count>0){
  14.                                
  15.                                 byte b =buf[pos];
  16.                                 count--;//每次读取是一个字节一个字节的读,读一次--一次
  17.                                 pos++;
  18.                                 return <font color="#ff0000">b&0xff</font>;//0xff == 255 == 1111 1111
  19.                         }
  20.                 return -1;
  21. }
复制代码
区别就在于代码中的红色代码。

那读取字节-1的问题解决了,又带来了另外一个问题。
当我们read()一次一个字节,返回值为int ,即我们将返回值提升了4个字节,那么我们输出的时候文件大小是否会比源文件大4倍?
可是我们发现复制的时候,文件大小并没有发生变化。
其实read()进行了一次转型。
同时write()的方法内部实行了一个类型强转的操作。例如前面的00000000 00000000 00000000 11111111
执行write()方法时它只获取指定的数据,前面的24位0将不会写入!所以保证了字节流复制文件的正确性。


这个例子虽然很小,但是所蕴含的知识非常细。学到了很多。

希望大家能从中受益!

评分

参与人数 1技术分 +1 收起 理由
黄炳期 + 1

查看全部评分

1 个回复

倒序浏览
高端大气牛B拽
虽然不是很明白 不过好厉害的样子
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马