bytes2int 和 int2bytes中的 & 0xff 操作详解

上一份代码先...

/**
*大端模式
*/
public static byte[] int2bytes(byte[] srcBytes , int target){
        byte[] bytes = new byte[srcBytes.length];
        int start = 24;
        for(int i = 0 ; i<srcBytes.length ; i++){
            bytes[i] = (byte) (( target >> ( start - i * 8 ) ) & 0xFF );
        }
        return bytes;
    }

/**
*小端模式
*/
public static byte[] int2bytes(byte[] srcBytes , int target){
        byte[] bytes = new byte[srcBytes.length];
        int start = 24;
        for(int i = 0 ; i<srcBytes.length ; i++){
            bytes[srcBytes.length - i - 1] = (byte) (( target >> ( start - i * 8 ) ) & 0xFF );
        }
        return bytes;
    }

其中涉及 原码、反码、补码的知识、简单聊下...

一个byte占一个字节(8位)

在计算中、最后存储的是补码


原码:

        对于正数,原码就是本身,如 十进制10,其原码 = 0000 1010

        对于负数,最高位(符号位)用1表示负数,如 十进制10,其原码 = 1000 1010

反码:

        对于正数,反码=原码,如十进制10 ,其反码 = 0000 1010

        对于负数,反码=原码取反(除了符号位外,0变为1,1变为0),如十进制10,其反码 = 1111 0101

补码(整数在计算机中的存储形式):

        对于正数,补码=反码=原码,如十进制10,其补码 = 0000 1010

        对于负数,补码 = 反码 + 1,如十进制10,其补码 = 1111 0101(补码+1进位而得)


探讨下,为什么补码才是最终的存储,为什么补码可以一枝独秀

陈独秀,请先做下...


假设有这么一个表达式,10 - 10,答案是 = 0毋庸置疑,我们分别用原码,反码,补码运算下...
10-10的原码操作:
    0000 1010
+   1000 1010
---------------
    1001 0100    等于十进制的-20

10-10的反码操作:
    0000 1010
+   1111 0101
---------------
    1111 1111   我们把反码再转回原码,符号位不变,取反后有,原码 = 1000 0000,等于十进制的 -0

10-10的补码操作:
    0000 1010
+   1111 0110
---------------
  1 0000 0000  一个byte占8位,多出来的就自动剔除了,<--注意此时剔除的为符号位-->,所以补码=0000 0000 ,再转成补码,减去1,有补码=1111 1111,再取反转成原码,有原码= 0000 0000,即十进制的 0。

---------------------------------------------
---------------华丽的分割线-------------------
---------------------------------------------

[+0]原码=0000 0000,   [-0]原码=1000 0000

[+0]反码=0000 0000,   [-0]反码=1111 1111

[+0]补码=0000 0000,   [-0]补码=0000 0000   

你会发现,+0和-0的补码是一样的。即 0的补码只有一种表示。

为什么呢?这就是刚刚上面说的,-0的反码在转成补码是,在符号位上发生了进位舍弃。

好、我们回到话题

<--为什么需要 & 0xFF 操作呢?-->

其实依赖于两个知识点:

1、计算机中存储的是补码

2、十六进制的0xff等于二进制的 1111 1111,进行逻辑&运算后,可以很好的扩展/缩减位数


我们分别举例   bytes2int   int2bytes  做分析


对于第一个知识点

举个bytes2int的小例子

public static void main(String[] args) {
        byte[] bytes = new byte[10];
        bytes[0]= 65;
        System.out.println(bytes[0]);
        int result = bytes[0] & 0xff;
        System.out.println(result);
}

跑一遍后,发现 
65
65
oxFF转成二进制 = 1111 1111
一个8位的二进制,跟0xFF做 & 运算不是还是本身吗?
似乎这个 & 0xff 的操作有些多余...

当我们输入一些边界例子后,比如让 bytes[0] = -127
在次跑之后,我们惊奇的发现控制台输出为:
-127
129

这是怎么回事?

不急
这里存在着类型自动提升原则,我们先讨论下

对于 十进制-127来说
byte类型的-127二进制有(8位):
原码 = 1111 1111
反码 = 1000 0000  (原码符号位不变,其他为取反而得)
补码 = 1000 0001

int类型的-127二进制有(32位):
原码 = 0000 0000 0000 0000 0000 0000 1111 1111
反码 = 1111 1111 1111 1111 1111 1111 1000 0000
补码 = 1111 1111 1111 1111 1111 1111 1000 0001


因为计算机最终存储的是补码形式。所以我们讨论下补码形式
我们知道,上面的两个补码不同,但对应的十进制是相同的,都是-127,如果不做 & 0xFF 操作的话,答案也就正确了,反而 & 0xFF后,结果变成了129.
我们想一下,平时我们在操作一些文件流的时候,先要把文件转成byte数组,在这个时候我们还是只关心byte数组对应的十进制在转换前后是否相同吗?
其实我们关心的应该是补码,毕竟计算机存的是补码

所以就体现出 & 0xFF的必要性了...
我们看下,在类型自动提升后的bytes[0],再与0xFF做逻辑与运算的时候
    1111 1111 1111 1111 1111 1111 1000 0001   <-- bytes[0] 转成 int类型了
&                                 1111 1111   <-- 0xFF
--------------------------------------------
                                  1000 0001
可以看到,做了逻辑与运算后的补码跟最初的bytes[0]的补码是一样的
到这我们也就明白了,为什么要做 & 0xFF操作了...
还是因为计算机存储的是补码形式...



下面的流程倒是有些无聊的 int2bytes了...

public static int2bytes(int value){
    byte[] src = new byte[4];
    src[0] = (byte) ((value >> 24) & 0xFF);
    src[1] = (byte) ((value >> 16) & 0xFF);
    src[2] = (byte) ((value >> 8) & 0xFF);
    src[3] = (byte) (value & 0xFF);
    return src;
}


假设value = 65;
                                           64  8421
65的二进制 = 0000 0000 0000 0000 0000 0000 0100 0001(int类型4个字节,32位)
    0000 0000 0000 0000 0000 0000 0000 0000   <-- value >> 24
&                                 1111 1111   <-- 0xFF
--------------------------------------------
                                  0000 0000   <-- ((value >> 24) & 0xFF)
                                 src[0] = 0;  <-- (byte) ((value >> 24) & 0xFF)

同理有:                          src[1] = 0;
                                 src[2] = 0;

对于最后的src[3]有;
    0000 0000 0000 0000 0000 0000 0100 0001   <-- value
&                                 1111 1111   <-- 0xFF
--------------------------------------------
                                  0100 0001   <-- ((value >> 24) & 0xFF)
                                 src[3] = 65

从而得:   src[4] = [ 0 , 0 , 0 , 65];







 

 

展开阅读全文

没有更多推荐了,返回首页