byte[i] & 0xFF原因(byte为什么要与上0xFF?)

byte[i] & 0xFF原因(byte为什么要&上0xff?)

  • 先来看一段代码:
private String changeHex(byte[] bs){
    char[] hexArray = "0123456789abcdef".toCharArray(); // 将字符串转换为一个字符数组
    char[] hexChars = new char[bs.length * 2]; // 创建一个bs字符数组两倍的字符数组
    for ( int j = 0; j < bs.length; j++ ) {
        int v = bs[j] & 0xFF; // 保持二进制补码的一致性 因为byte类型字符是8bit的  而int为32bit 会自动补齐高位1  所以与上0xFF之后可以保持高位一致性 当byte要转化为int的时候,高的24位必然会补1,这样,其二进制补码其实已经不一致了,&0xff可以将高的24位置为0,低8位保持原样,这样做的目的就是为了保证二进制数据的一致性。
        hexChars[j * 2] = hexArray[v >>> 4];
        hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
    }
  • 简单分析这段代码:
    首先这段代码的作用显而易见:将java中的byte类型转换为String类型~分析其实大致已经在代码中给出注释,&这里先暂且不提,我们先看看>>>这个移位操作符,因为平时自己用的不多,写到这里就纯粹当做给自己补充下知识吧。。
    java中有三种移位运算符:
<<      :     左移运算符,右边空出的位用0填补,高位左移溢出则舍弃该高位:num << 1,相当于num乘以2
>>      :     右移运算符,左边空出的位用0或者1填,正数用0负数用1填,低位右移溢出则舍弃该位。num >> 1,即num除以2
>>>     :     无符号右移,忽略符号位,空位都以0补齐
  • 接下来我们看一个小Demo
package com.czc;
import org.junit.Test;
public class ThreadTest {
    @Test
    public void Test() {
        byte[] a = new byte[10];
        a[0] = -127;
        a[1] = -128;
        System.out.println(a[0]);
        int c = a[0] & 0xFF;
        System.out.println(c);
        a[2] = (byte) (a[0]<<1);
        System.out.println(a[2]);
        a[3] = (byte) (a[0]>>1);
        System.out.println(a[3]);
    }       
}

很简单的一个小Demo,嗯,接下来,先看这个小Demo,再回头去看之前的那个byte类型转String类型的&

  • 看看Demo执行结果
-127
129
2
-64

嗯,结果似乎有不少地方与我们所想似乎并不一致,好了,这里我们还需要有起码的认识才行.

  • 这里我们还需要知道的是:
    在计算机系统中,数值一律用补码来表示(存储)。 主要原因:使用补码,可以将符号位和其它位统一处理;同时,减法也可按加法来处理。
    机器数:一个数在计算机中的二进制表示形式, 叫做这个数的机器数。机器数是带符号的,在计算机用一个数的最高位存放符号, 正数为0, 负数为1.
    真值:因为第一位是符号位,所以机器数的形式值就不等于真正的数值。例如有符号数 10000011,其最高位1代表负,其真正数值是 -3 而不是形式值131(10000011转换成十进制等于131)。所以,为区别起见,将带符号位的机器数对应的真正数值称为机器数的真值。
    原码:原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值.
    反码:反码的表示方法是:正数的反码是其本身,负数的反码是在其原码的基础上, 符号位不变,其余各个位取反.
    补码:补码的表示方法是:正数的补码就是其本身,负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)
  • 庖丁解牛
    既然知道了数据在计算机系统中是以补码的形式存储,那么上面a[0]的在计算机系统中存储的值呼之欲出,真值为-1111111,原码则为11111111,反码不多说,则其补码则为:10000001,所以第一个输出a[0]为-127是没有问题的,然后就是说到&的情况,a[0] & 0xFF 也即是 10000001&11111111 其结果不言而喻 还是它自己本身 那么为什么输出的结果却是129呢,原因在于,我们需要把byte类型转为int类型输出,将a[0] 作为int类型向控制台输出的时候,jvm作了一个补位的处理,因为int类型是32位所以补位后的补码就是1111111111111111111111111 10000001(32位),这个32位二进制补码表示的也是-127,发现没有,虽然byte->int计算机背后存储的二进制补码由10000001(8位)转化成了1111111111111111111111111 10000001(32位)很显然这两个补码表示的十进制数字依然是相同的。接下来才是继续和0xFF做&运算,即a[0]&0xff=1111111111111111111111111 10000001&11111111=000000000000000000000000 10000001 ,这个值算一下是129!

好,接下来看移位运算符,知道了补码存储之后,其实移位运算就是很显然的事情了,这里不再多说~

  • 回归正题
    其实,看完上面的Demo,大家其实心里应该已经有了一点谱了,接着我们思考下,做byte->int的转化,所有时候都只是为了保持 十进制的一致性吗?
    不一定吧?好比这次项目拿到的文件流转成byte数组,难道我们关心的是byte数组的十进制的值是多少吗?我们关心的是其背后二进制存储的补码,所以大家应该能猜到为什么byte类型的数字要&0xff再赋值给int类型,其本质原因其实就是想保持二进制补码的一致性。
    当byte要转化为int的时候,高的24位必然会补1,这样,其二进制补码其实已经不一致了,&0xff可以将高的24位置为0,低8位保持原样。这样做的目的就是为了保证二进制数据的一致性。当然了,保证了二进制数据性的同时,如果二进制被当作byte和int来解读,因为符号位位置已经发生了变化,所以其10进制的值也必然是不同的。

  • 答疑
    有人问为什么上面的式子中a[0]不是8位而是32位?这是因为当系统检测到byte可能会转化成int或者说byte与int类型进行运算的时候,就会将byte的内存空间高位补1(也就是按符号位补位)扩充到32位,再参与运算。上面的0xff其实是int类型的字面量值,所以可以说byte与int进行运算。

你可能感兴趣的:(Java,web)