其实一点都不奇怪,下面我们来详细分析
首先看下面一段代码,代码的意思是将128写入到文件中,再从文件中读出一个byte输出
File f = new File("f1");
FileOutputStream fos = new FileOutputStream(f);
fos.write(128);
fos.close();
FileInputStream fis = new FileInputStream(f);
int a = fis.read();
System.out.println(a);
输出结果为:
128
这符合我们的直观感觉,写入128,读出来也是128
下面我们修改一下代码,将数据度入到byte数组里,看看输出会是什么样子,代码如下
File f = new File("f1");
FileOutputStream fos = new FileOutputStream(f);
fos.write(128);
fos.close();
FileInputStream fis = new FileInputStream(f);
byte[] b = new byte[1];
fis.read(b, 0, 1);
System.out.println(b[0]);
输出结果为
-128
好了,“奇怪”的现象出现了,这是为什么呢?
首先,我们需要了解一些基础知识,在Java中,byte的范围为 -128 ~ 127,并没有128这个数值;其次,读者需要了解一下负数在Java中如何表示(即原码、反码、补码的相关知识,请读者自行谷歌)。
下面,我们来分析一下出现 -128
的原因:
首先,
fos.write(128)
表示写入 “128” 所代表的字节,即1000 0000
,也就是说,文件中当前包含一个字节,8位依次为 1 0 0 0 0 0 0 0当我们调用
fis.read()
时,该方法会返回一个无符号byte,也就说直接将1000 0000
翻译为128,大家可以去看该方法的说明,返回值的范围为0 ~ 255;所以我们得到了 128 的输出;当我们调用
fis.read(b, 0, 1)
时,相当于把文件中第一个字节读入到了b[0]中,此时b[0]的二进制表示为1000 0000
,根据负数在Java中的表示方法,我们可以计算出,该二进制序列的数值为 -128,也就是说这个过程并没有将其转化为无符号数值的过程,所以我们得到了 -128 的输出。
那么,我们如何解决这个问题呢?
首先,我们需要看一下fos.write(int a)
这个方法,它的功能是写入a代表的byte值,也就是说,该函数实际上是将a 的后八位写入到文件中 , 所以作者建议在使用该方法时,为了避免产生歧义,a 的范围最好在 0 ~ 255 之间,初学者最好在 0 ~ 127 之间。(具体原因可以结合负数的表示规则自己想想哦~)。
回归正题,我们如何解决上文中的问题呢,即 “写入128、读出-128” 的问题,我们可以用下面的代码来解决。
File f = new File("f1");
FileOutputStream fos = new FileOutputStream(f);
fos.write(128);
fos.close();
FileInputStream fis = new FileInputStream(f);
byte[] b = new byte[1];
fis.read(b, 0, 1);
System.out.println(b[0] & 0xff);
也就是说,用
b[0] & 0xff
的方法得到b[0]的无符号值。插入一个小问题,就是,
b[0] | 0xff
得到的是一个负值,而 & 得到的却是一个正值。这里根据作者的猜测,可能是如下原因,主要和 & 运算的步骤有关:进行
b[0] & 0xff
运算时的步骤如下:
- 因为0xff默认应该用int来保存,所以首先将 b[0] 强制转化为 int 类型表示,b[0] 的数值为-128,所以其二进制表示为11111111 11111111 11111111 10000000
- 0xff的二进制表示为 00000000 00000000 00000000 11111111
- 这两个数做 & 运算后,得到 00000000 00000000 00000000 10000000,其表示的值为128
“或 运算”的过程类似,读者可以自己推理一下