无参read返回int类型为何要与上0xff

引言

使用java io 包时,InputStream 类中的有好几个read()方法,并且返回值也都是int类型,这样就使得初学者很容易搞混,其实虽然返回值都是int类型,但所表示的意义确是不一样的

有参 read(byte b[], int off, int len)

先看有参的read方法。
有参的read比较好理解,将读取到的数据写入字节数组(从字节数组的指定位置开始写入)

  • 三个入参
    • b[] 存储读取到的数据的字节数组
    • off 从目标数组b[]off位开始写入,一般都是从0开始
    • len 要读取的字节码长度,一般会是存储数组b[]的长度

这里的int类型的返回值是实际读取的字节数,如果检测到无数据可读时会返回 -1

无参 read()

/**
 * 从输入流读取下一个字节的数据。返回值为`int类型`,值范围在`0-255`之间。如果由于到达流
 * 的末尾而没有可用的字节,则返回`-1`。此方法将一直阻塞,直到输入数据可用、检测到流的结
 * 尾或引发异常为止
 */
public abstract int read() throws IOException;

从输入流读取下一个字节的数据。返回值为int类型,值范围在0-255之间。如果由于到达流的末尾而没有可用的字节,则返回-1。此方法将一直阻塞,直到输入数据可用、检测到流的结尾或引发异常为止

我们再来看一下这个方法的具体实现

public int read() throws IOException {
    if (eof) {
        return -1;
    }
    temp = new byte[1];
    int n = read(temp, 0, 1);
    if (n <= 0) {
        return -1;
    }
    return temp[0] & 0xff;
}

到这里就很奇怪了,这个方法是返回下一个字节的数据,可是为什么要返回一个int类型,而不直接返回 byte 类型?并且返回int类型时还有一个& 0xff操作,为什么还要执行这个与操作呢?下面就让我们好好分析一下,为何要返回int类型,而不直接返回byte类型,以及为何会先执行一个& 0xff操作

为何要返回int类型

在读取字节时,我们肯定需要一个标识来表示已经读到了字节流末尾,一般会返回-1来标识,但是如果是返回byte类型,就无法标识是否到了文件末尾。所以单凭这点,这里就不能返回byte类型

为何要执行与操作& 0xff再返回int类型

上面解释了为何要返回int类型,可是貌似返回int类型也并没有解决问题..

看这么一个情况,万一返回的单个字节以二进制表示是1111 1111,转换成int类型会高位补符号位1,也就是1111 1111 1111 1111 1111 1111 1111 1111,刚好是-1(Java中数字是以补码形式存储的),所以这里就会产生混乱,我们无法区分返回-1是不是到了字节流的末尾

那么这个问题该怎么解决呢?
这里要解决的问题其实就是当还没有读到字节流末尾时不能返回-1,并且二进制字节流也不能改变
所以这里就加入了& 0xff操作,我们再来看前面返回-1的例子
1111 1111高位自动补1后与上0xff(0000 0000 0000 0000 0000 0000 1111 1111)

1111 1111 1111 1111 1111 1111 1111 1111 & 0000 0000 0000 0000 0000 0000 1111 1111 = 0000 0000 0000 0000 0000 0000 1111 1111

可知执行了& 0xff操作后,相当于永远不会返回负数了,也就不会存在返回-1和到字节流末尾返回-1冲突的情况,并且实际的二进制结构也没有改变,可以说是完美的解决了这个问题。

上面的1111 1111会自动高位补符号位的原因是,当Java检测到byte要转化或将要转换成高位类型时,会自动补高位符号位

补符号位

这里再解释一下补符号位
我们知道byte占一个字节8位,而int类型占4个字节32位,所以byte类型向上转换成int类型时需要补符号位,正数补0,负数补1。我们补符号位的目的是为了类型转换后大小和符号位都保持不变。

总结

本篇文章虽然是从IOread方法接入,其实还是跟Java内部的编码格式有关,如数字在Java内存中是以补码存储的,数字类型转换会高位补符号位等等,要彻底搞懂还是要花些时间的。

博客原文地址戳这里

你可能感兴趣的:(无参read返回int类型为何要与上0xff)