Java中byte做&0xff运算的原因及解析

网上瞎写的一大堆,实在是无语。把自己理解的整理分享给大家。
首先要知道原码、反码、补码是什么。可以参考:
http://www.cnblogs.com/zhangziqiu/archive/2011/03/30/ComputerCode.html

第一种情况:signed/unsigned之间的转化

java中,除了char是unsigned 两个字节,用来表达UTF-16,此外byte/short/int/long都是signed的。
取值范围:
boolean:一字节
byte:(-128,127) 一字节
char:(0,65535) 两字节
short:(-32768,32767)两字节

那么在Java与别的语言编写的程序通信时,就可能涉及到signed/unsigned之间的转化。比如C写的socket传输unsigned数据

signed到unsigned的转化

//JDK 1.8中Byte.toUnsignedInt(byte x)
public static int toUnsignedInt(byte x) {
        return ((int) x) & 0xff;//将高24位全部变成0,低8位保持不变
}

//JDK 1.8中Integer.toUnsignedLong((int x)
public static long toUnsignedLong(int x) {
        return ((long) x) & 0xffffffffL;//将高32位全部变成0,低32位保持不变
}

比较典型,经典的运用则是下面的:

//JDK1.8 ByteArrayInputStream  read函数源码

protected byte buf[];//用于缓存数据的数组

//调用该函数会返回一个(0,255)的int类型数字,如果读到stream的end,则返回-1
public synchronized int read() {
        return (pos < count) ? (buf[pos++] & 0xff) : -1;//这里跟调用toUnsignedInt(byte x)的效果是一样的
}

上面的源码可能有的人会犯迷糊
0xff的二进制是1111 1111 (8bit),一个byte也是8bit,
上面的操作buf[pos++] & 0xff是将一个byte类型的数字&0xff,那么得到的结果应该是还是这个byte类型数字本身呀?

可能你忽略了一个问题了:Java二元运算符的类型自动提升
也就是说buf[pos++] & 0xff这个运算中,buf[pos++]已经被自动提升为int了。

byte b = -20;
System.out.println(b & 0xff);// 236

final byte b2 = -20;
final byte mask = (byte) 0xff;
System.out.println(b2 & mask); // -20

第二种情况: byte转化为16进制String

在如下一段代码中有int v = src[i] & 0xFF这样的代码。

 public static String bytesToHexString(byte[] src){
        StringBuilder stringBuilder = new StringBuilder("");
        if (src == null || src.length <= 0) {
            return null;
        }
        for (int i = 0; i < src.length; i++) {
            int v = src[i] & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }
        return stringBuilder.toString();
    }

这里做&0xff的原因是:
1.byte是1byte(8位),int是4byte(32位)表示的。
2.Java中是使用了补码的形式进行数据存储的。
3.java中byte数据转化为int数据时会自动补位,如果最高位(符号位)是0,则高24位全部补0,若是1,则高24位全部补1。
4.知道了上面的三点,就有:
byte -1的二进制为(补码):1111 1111 -->对应的16进制为0xFF
int -1的二进制为(补码):1111 1111 1111 1111 1111 1111 1111 1111 -->对应的16进制为0xFFFFFFFF
5.Integer.toHexString(int i)函数内部是通过传进来数字的二进制(补码形式)来进行转换的,因此如果不进行int v = src[i] & 0xFF;
则得到的结果就是0xFFFFFFFF。而&0xff只后,传入的参数的二进制就变为0000 0000 0000 0000 0000 0000 1111 1111(虽然这个数的值以不再是-1,但是将他进行转换得到的0xff才是我们需要的)
源码如下:

//JDK1.8
static int formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) {
        int charPos = len;
        int radix = 1 << shift;
        int mask = radix - 1;
        do {
            buf[offset + --charPos] = Integer.digits[val & mask];//每次do-while循环都会取4位(从高位到低位),Integer.digits[]数组是十六进制的字符集
            val >>>= shift;
        } while (val != 0 && charPos > 0);

        return charPos;
    }

final static char[] digits = {
        '0' , '1' , '2' , '3' , '4' , '5' ,
        '6' , '7' , '8' , '9' , 'a' , 'b' ,
        'c' , 'd' , 'e' , 'f' , 'g' , 'h' ,
        'i' , 'j' , 'k' , 'l' , 'm' , 'n' ,
        'o' , 'p' , 'q' , 'r' , 's' , 't' ,
        'u' , 'v' , 'w' , 'x' , 'y' , 'z'
    };

6.以上五点是全部原因,end

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值