写这篇文章的起因
本来今天在写浅析分布式唯一ID生成算法——snowflake的实现这篇博客的时候,写的好好的,但是写到一种生成方式
UUID.randomUUID().toString(); |
发现自己并不是很知道这个怎么实现的,然后点开了源码打算读一下。结果就发现了一段代码。
/* |
其中有一小段(下方代码段)我不是很理解有什么作用,因为data[i]的类型实际上是byte类型的,byte的十进制范围是-128-127,而0xff的十进制是15 * 16 + 16 = 256,转化为八位二进制就是11111111,我想着data[i] & 0xff的话是使用0xff做截取操作,但是byte转化为二进制的长度肯定在8位以内,所以这个截取操作第一眼看起来并没有什么用哈?
data[i] & 0xff |
但是看着这个写法感觉好像有些熟悉,因为之前写md5加解密好像看到过类似的代码,但那个时候是因为赶作业,所以就没有深究。我找回了当时的代码。代码地址如下——代码地址
同样也有类似的写法,但是这个代码里的写法又有些不一样。因为这里写的是0x000000ff,那就是32位。
我这才意识到原来0xff是一个int类型的值。那么完整的写就是0x000000ff,任何数与0x000000ff做截取的结果就是令高24位都为0,保留低八位。
而data[i]作为一个byte类型的值。比如data[i] = -127,并且byte类型是有符号类型。那么-127的8位二进制补码表示就是0x10000001,但是事实上byte和int做&运算的时候byte若为负数,那么data[i]会转型为int,转化后的高24位都会为1,这个时候转换为int后的data[i]和原来的data[i]他们的源码十进制数虽然相同,但是事实上他们的二进制补码已经发生了改变。
用下面的代码举个例子(Integer.toHexString()的作用就是查看他们的16进制补码)
byte a = -128; |
结果为
-25600 |
可以看到对于正数的byte类型,和int互操作不会有什么影响;但是对于负数的byte,若是不对其进行& 0xff的话,转化后的值的补码前24为均为1(结果中的ffff9c00)。
所以说到这里已经很明白了,使用0xff做截取转换的作用就是防止byte为负时与int进行计算,从而令byte先转化为高24位均为1(补码)的int。虽然这样使用0xff截取后,byte转化为int后的十进制并不等于原byte的十进制,但是却保证了他们的二进制补码的一致性。