网上瞎写的一大堆,实在是无语。把自己理解的整理分享给大家。
首先要知道原码、反码、补码是什么。可以参考:
http://www.cnblogs.com/zhangziqiu/archive/2011/03/30/ComputerCode.html
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数据
//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
在如下一段代码中有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