强制转换的一个问题

这个问题本来没什么值得关注,但是强转如果发生溢出,结果会有些奇怪。加上我是在写协议的校验和函数时遇到这个问题的,相关的概念很容易让人头大
先上结论:
1.源代码中所有数值均为其真值,例如可以看到有负号”-“
2.机器存储的只有补码
3.强制转换结果是使用补码来截断得到的(不包括浮点型数)
4.打印出的是十进制的真值
5.校验和使用的是无符号数
6.java中没有无符号数,可以使用高一级的类型表示低一级的无符号数(因为正数的补码与原码相同)

下面看一下强制转换的例子

1
2
System.out.println((byte)0x81);
System.out.println((byte)-0x81);//注意有个负号

上面的两个语句分别会输出:-127127,现在来解释一下:
在java中,整数数值默认是int类型,第3点提到要要用补码截断,int型数值0x81的补码是0x00000081,截断的结果是只保留其最后8位,即剩下0x81,即二进制的10000001,这是补码,对应的原码为11111111,其十进制的真值-127
-0x81的补码为0xFFFFFF7F,截断的结果是0x7F,即二进制的01111111,这是补码,而由于符号位是0,对应的原码就是其本身,故其十进制真值为127

再来看这两个函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* 由byte数组得到其校验和
* @param buf 数据
* @return 校验和存储在long型返回值的低16位
*/
private static long getCheckSum(byte[] buf) {
int i = 0;
int length = buf.length;
long sum = 0;//checksum只占sum的低16位
//由于sum为long型,下面的加法运算都不会导致符号位改变,等价于无符号加法
while (length > 0) {
sum += (buf[i++] & 0xff) << 8;//与checksum的高8位相加
if ((--length) == 0) break;// 如果buf的byte个数不为偶数
sum += (buf[i++] & 0xff); //与checksum的低8位相加
--length;
}
//处理溢出,将sum的从右往左第二个16位与其第一个16位(最右的16位)相加并取反
return (~((sum & 0xFFFF) + (sum >> 16))) & 0xFFFF;
}
/**
* 取value的后几个字节放入byte[]中
* @param value
* @param len 指定value的后len个字节
* @return
*/
private static byte[] toBytes(long value, int len) {
byte[] b = new byte[len];
for (int i = 0; i < len; i++) {
b[len - i - 1] = (byte) (value >> 8 * i);
}
return b;
}

第一个函数的目的是得到byte[]数据的校验和,然而java没有无符号数据,所以变量sum用的是long类型,因此在加法运算时,byte数据都会被提升为long类型,所以函数中加法的效果与无符号数加法相同

第二个函数来自http://blog.csdn.net/tang9140/article/details/43404385,其目的是得到一个整数型数据得到后len个字节,函数的关键是利用强制转换得到最后8位,然后右移8位,直到得到len个字节。这是可行的但是如果要从byte数组取出原来类型的数,也要对应的转化,详见原文的处理函数