字节数组和基本类型转换

基础知识
运算符顺序
boolean 一位
byte 8位 一个字节
short char 2个字节
int float 4个字节
double long 8字节

java是unicode编码,一个char可以占两个字节,可以存储一个汉字。一个String看编码 如果是utf-8 要看有几个字母或者几个汉字,一个英文字母就是一个字节,一个汉字就是3个字节,以此类推计算。

我们知道,计算机是以补码的形式存放数值型数据,正数的补码,是其本身。负数的补码是对应正数的原码取反,再加1。所以负数对应正数原码的一种正确求法应该是:应该是先减1 再取反
-9对应正数的原码为00001001,因为是负数,补码为最高位置1,其余取反也就是11110110,然后在最低位加1即即11110111。反过来求-9对应正数9的源码即 先-1为 11110110再取反为00001001 (最高位始终为0)
若对char,byte 或者short 进行移位处理,那么在移位进行之前,它们会自动转换成一个int(32位),这个byte型会先自动补全到32位(即一个int型),再进行移位操作
举个例子:一个byte型的-1,在内存中的补码是八个1:11111111,当我们进行移位时,它会进行补全,而且是有符号位的补全,再左移8位,
所以最后结果是:11111111 11111111 11111111 00000000,
(上例中我们左移8位,需要将11111111 11111111 11111111 00000000去掉前面的那些1,只保留次低位上的1,即为:0000000000000000 11111111 00000000)。
&运算小技巧:1不影响与运算, 如一个int类型的数值 i, 与 0x000000ff(0xff),进行&与运算,可以保留int值最后一位字节上的数值,而将前面的数值全部变为0.然后再进行移位运算,就可以很顺利的消除首尾的符号位的影响了。
|运算小技巧: 0不影响或运算,可以快速的对字节进行拼接,有1就为1,但是注意不进位奥,仅仅是当前位,有1就为1.

在这里插入图片描述

带符号按位左移 << 带符号右移>> 不带符号右移 >>>

助记: 右侧补0,左侧补1

1.内存中的位运算,都会先转换成int,4个字节进行运算,左侧不足用符号位补齐
2.<< n : 整体向左移动 n 位,右侧每位用0补齐
3.>> n : 整体向右移动 n 位,左侧每位用符号位补齐
4.>>>n : 整体向右移动 n 位,左侧用0补齐
没有<<<n
-9在计算机中存储为11110111(0xf7) java中占4个字节,即0xfffffff7(不够位数的用符号位补充)。
向左左移4位那么,右侧的4个低位用0补充。为ffffff70
向右移4位那么,左侧的4个高位用4个符号位 1补充。为0xffffffff(4个字节32位16进制一个字符如f占4位)
向右移2位那么,左侧的2个高位用2个符号位 1补充。为0xfffffffd

System.out.println("-9向左移4位:"+Integer.toHexString(-9 << 4));
System.out.println("-9向有右移4位:"+Integer.toHexString(-9 >> 4));
System.out.println("-9向有右移2位:"+Integer.toHexString(-9 >> 2));

结果

-9向左移4位:ffffff70
-9向有右移4位:ffffffff
-9向有右移2位:fffffffd

位 & | ^
1.位& : 按位相乘,有0就为0,1不影响与运算
2.位| : 按位相加,有1就是1,0不影响或运算
3.位^ : 相同为0,不同位1
十六进制的表现形式 0x…,与内存中的存储形式无关(内存中是以二进制存储的),16进制一个字符如f占4位。

内存中的位运算,都会先转换成int,4个字节进行运算,左侧不足用符号位补齐:
与或运算的 默认(不强转)都是int类型。即4个字节进行运算,计算结果还是int 。强转为long与long 与 或结果是long 强转为shot与shot 与 或结果是long

例证如下:

   public static int toUnsignedInt16(byte[] buffer, int offset, boolean bigEndian) {
        if (bigEndian) {
            return (0xff & buffer[offset + 1])
                    | (buffer[offset] << 8);
        } else {
            return (0xff & buffer[offset])
                    | (buffer[offset + 1] << 8);
        }
    } 
    public static short toInt16(byte[] buffer, int offset, boolean bigEndian) {
        if (bigEndian) {
            return (short) ((0xff & buffer[offset + 1])
                    | (buffer[offset] << 8));
        } else {
            return (short) ((0xff & buffer[offset])
                    | (buffer[offset + 1] << 8));
        }
    }
    public static char toChar(byte[] buffer, int offset, boolean bigEndian) {
        if (bigEndian) {
            return (char) ((0xff & buffer[offset + 1])
                    | (buffer[offset] << 8));
        } else {
            return (char) ((0xff & buffer[offset])
                    | (buffer[offset + 1] << 8));
        }

    }

// long |long ->long
 public static long toInt64(byte[] buffer, int offset, boolean bigEndian) {
        if (bigEndian) {
            return ((((long) buffer[offset]) << 56) |// 向左移位 右侧用0补齐 符号位不用 &0xff 
                    (((long) buffer[offset + 1] & 0xff) << 48) |
                    (((long) buffer[offset + 2] & 0xff) << 40) |
                    (((long) buffer[offset + 3] & 0xff) << 32) |
                    (((long) buffer[offset + 4] & 0xff) << 24) |
                    (((long) buffer[offset + 5] & 0xff) << 16) |
                    (((long) buffer[offset + 6] & 0xff) << 8) |
                    (((long) buffer[offset + 7] & 0xff)));
        } else {
            return ((((long) buffer[offset + 7]) << 56) |
                    (((long) buffer[offset + 6] & 0xff) << 48) |
                    (((long) buffer[offset + 5] & 0xff) << 40) |
                    (((long) buffer[offset + 4] & 0xff) << 32) |
                    (((long) buffer[offset + 3] & 0xff) << 24) |
                    (((long) buffer[offset + 2] & 0xff) << 16) |
                    (((long) buffer[offset + 1] & 0xff) << 8) |
                    (((long) buffer[offset] & 0xff)));
        }

    }

多字节向少字节强转:如当你将一个int型强制类型转换为byte型的时候,最高的三个字节会被砍掉,只留下最低的8位赋值给byte型。高字节被舍去。
少字节类型向多字节强转:如当你将一个byte型强制类型转换为int型的时候,最高位会补符号位。

public class Test {
  public static void main(String[] args) {
     int a = 60; /* 60 = 0011 1100 */ 
     int b = 13; /* 13 = 0000 1101 */
     int c = 0;
     c = a & b;       /* 12 = 0000 1100 */
     System.out.println("a & b = " + c );
 
     c = a | b;       /* 61 = 0011 1101 */
     System.out.println("a | b = " + c );
 
     c = a ^ b;       /* 49 = 0011 0001 */
     System.out.println("a ^ b = " + c );
    /*-61 = 1100 0011 负数存放是以补码形式存放 
      最高位不变,其余取反再加1 所以求绝对值应该是先减1 除了最高位再取反
      00111101=60 */
     c = ~a;         
     System.out.println("~a = " + c );
 
     c = a << 2;     /* 240 = 1111 0000 */
     System.out.println("a << 2 = " + c );
 
     c = a >> 2;     /* 15 = 1111 */
     System.out.println("a >> 2  = " + c );
  
     c = a >>> 2;     /* 15 = 0000 1111 */
     System.out.println("a >>> 2 = " + c );
  }
} 

大小端

百度百科对大小端模式解释:
因为除了bool byte类型,其他类型都是多余一个字节,所以涉及到字节在内存的排序的问题,即高位在存在低地址还是高地址中,即所谓的大小端问题。实际应用中,大小端应用的地方很多通信协议、数据存储等。如果字节序不一致,就需要转换。不知道大家对数组(如byte数组)进行强制转换成整型数据(或者相反转化)没有?如果你要进行强制转换,肯定要考虑大小端问题。 跟语言和框架 cpu都有关系如,java默认是小端模式。Netty中默认是大端。
先说结论:
0000430: e684 6c4e 0100 1800 53ef 0100 0100 0000
在大端模式下,前32位应该这样读: e6 84 6c 4e ( 假设int占4个字节),和阅读顺序一致。
在小端模式下,前32位应该这样读: 4e 6c 84 e6( 假设int占4个字节)。
cpu与大小端:
1.常见CPU的字节序
大端模式:PowerPC、IBM、Sun
小端模式:x86、DEC。STM32属于小端模式
操作系统与大小端:
不同的操作系统有不同的存放方式,常见的操作系统是小端
通讯协议与大小端:
通讯协议是大端。网络字节序,TCP/IP各层协议将字节序定义为Big-Endian,因此TCP/IP协议中使用的字节序通常称之为网络字节序。网络传输中采用的大端标记法,也就是说先传比较高值的数字,就像 12一样,先传10,在权传2,就算丢了后面一个,损失也不是太大。
语言与大小端
ByteOrder.nativeOrder()我在程序运行了一下,显示java是小端
代码如下

try {
            unsafe.putLong(a, 0x0102030405060708L);
            byte b = unsafe.getByte(a);
            switch (b) {
            // 阅读顺序一致
            case 0x01: byteOrder = ByteOrder.BIG_ENDIAN;     break;
            case 0x08: byteOrder = ByteOrder.LITTLE_ENDIAN;  break;
            default:
                assert false;
                byteOrder = null;
            }
        } finally {
            unsafe.freeMemory(a);
        }

大端模式,是指数据的高位字节保存在内存的低地址中,而数据的低位字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;这和我们的阅读习惯一致。
小端模式,是指数据的高位字节保存在内存的高地址中,而数据的低位字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低。和我们的逻辑顺序一致。

简单来说:big-endian 是高位的放在内存低地址处,低位的放在高地址处。
little-endian 是高位的放在内存高地址处,低位的放在低地址处。

如整数127(十进制)在计算机(64位)中大/小端字节序
在这里插入图片描述
数组在大端小端情况下的存储
规约:数组的偏移越大,代表地址越高。
下面以unsigned int value = 0x12345678为例,分别看看在两种字节序下其存储情况,我们可以用unsigned char buf[4]来表示value
Big-Endian: 低地址存放高位,如下:
高地址
  ---------------
  buf[3] (0x78) – 低位
  buf[2] (0x56)
  buf[1] (0x34)
  buf[0] (0x12) – 高位
  ---------------
  低地址
Little-Endian: 低地址存放低位,如下:
高地址
  ---------------
  buf[3] (0x12) – 高位
  buf[2] (0x34)
  buf[1] (0x56)
  buf[0] (0x78) – 低位
  --------------
低地址
内存地址 小端模式存放内容 大端模式存放内容
0x4000 0x78 0x12
0x4001 0x56 0x34
0x4002 0x34 0x56
0x4003 0x12 0x78
不同数值类型转换
先了解一下Java中int型与byte型数组之间的相互转换。
  首先,我们先来看看int型转换成byte型数组。
我们知道,Java中,一个int型占用4个字节,一个byte型占用1个字节,所以,对于一个int型,我们需要一个长度为4的byte型数组来对其进行存储。  
在这里插入图片描述

一个int型的4个字节如上图所示,假设用来存储的字节数组为bytes[],且为小端模式,那么,我们可以用bytes[0]存储int型的第一个字节(7位——0位),bytes[1]存储int型的第二个字节(15位——8位),bytes[2]存储int型的第三个字节(23位——16位),bytes[3]存储int型的第四个字节最高位(31位——24位)。具体代码如下:

1.其他类型转byte数组

public static byte[] int2BytesLittleEndian(int integer)
{
    byte[] bytes=new byte[4];
    // integer>>24 为int型 当你将一个int型强制类型转换为byte型的时候,最高的三个字节会被砍掉,只留下最低的8位赋值给byte型
    bytes[3]=(byte) (integer>>24);//右移24位,左侧补符号位,转成byte,保留原来最高8位,放在数组最高的字节中,代表最高位。为小端。
    bytes[2]=(byte) (integer>>16);
    bytes[1]=(byte) (integer>>8);
    bytes[0]=(byte) (integer);

    return bytes;
}

    public static byte[] int2BytesBigEndian(int integer)
    {
        byte[] bytes=new byte[4];
        bytes[0]=(byte) (integer>>24);//右移24位,转成byte,保留原来最高8位,放在最低的字节中,代表最低位。为大端。
        bytes[1]=(byte) (integer>>16);
        bytes[2]=(byte) (integer>>8);
        bytes[3]=(byte) (integer);

        return bytes;
    }
     System.out.println("小端");
        for(byte b:int2BytesLittleEndian(9)){
            System.out.println((Integer.toHexString(b)));
        }

        System.out.println("大端");
        for(byte b:int2BytesBigEndian(9)){
            System.out.println((Integer.toHexString(b)));
        }

输出 小端 9 0 0 0 大端 0 0 0 9

这里需要注意的是,当你将一个int型强制类型转换为byte型的时候,最高的三个字节会被砍掉,只留下最低的8位赋值给byte型。
接下来,我们来看一下byte型数组转换成int型。
  
 2. byte数组转其他类型
 计算技巧:
 我们可以先将byte数据元素与0xff 先进行按位与运算( & ),再进行移位来去除前面的符号位,最后进行 或| 运算。byte型数组转换成int型的代码如下:

public static int bytes2IntLittleEndian(byte[] bytes)
{
    //如果不与0xff进行按位与操作,转换结果将出错,有兴趣的同学可以试一下。
    int int1=bytes[0]&0xff;
    int int2=(bytes[1]&0xff)<<8;
    int int3=(bytes[2]&0xff)<<16;
    int int4=(bytes[3]&0xff)<<24;// byte[3]最高字节代表最高位为小端

    return int1|int2|int3|int4;
}

 public static int bytes2IntBigEndian(byte[] bytes)
 {
        //如果不与0xff进行按位与操作,转换结果将出错,有兴趣的同学可以试一下。
        int int1=bytes[3]&0xff;
        int int2=(bytes[2]&0xff)<<8;
        int int3=(bytes[1]&0xff)<<16;
        int int4=(bytes[0]&0xff)<<24;// byte[0]最高字节代表最高位为大段

        return int1|int2|int3|int4;
  }
  // 大端9转化
  byte[] b=new byte[]{0x00,0x00,0x00,0x09};// 大端的9和阅读顺序一致 高字节存低位 目标转化出来为9
  System.out.println("大端转化 "+bytes2IntBigEndian(b));
  System.out.println("小端转化 "+bytes2IntLittleEndian(b));

结果: 大端转化 9 小端转化 150994944

其他类型转化 byte[]=>unsighedInt32

我们因为最后在将byte型数组转换成int型的时候,需要对数组元素使用按位或( | )操作,因此,移位结果前面的符号位如果不去除(去除方式是按字节进行与&运算,后边将byte[]转成int会使用),将影响我们的运算,得出一个错误的结果。

    public static long toUnsignedInt32(byte[] buffer, int offset, boolean bigEndian) {
        if (bigEndian) {
        // 低字节在高位
        // 1.强转运算符比&优先级高,所以先转成long。将buffer[offset] 1个字节扩展成8字节,,如果是正数补0不用考虑无符号位,如果是负数有符号位扩展位数,例如如果是-1 一个字节为0xff 强转long 带符号补足64位 后为0xffffffffffffffff。
        // 2.&0xff 消除了符号位
        // 3.<< 24左移24位 对应下边代码
            // long a = (long) buffer[offset];
            // long b = a & 0xff;
            // long c = b << 24;
            // long d = (((long) buffer[offset] & 0xff) << 24);
            return ((((long) buffer[offset] & 0xff) << 24) |
                    (((long) buffer[offset + 1] & 0xff) << 16) |
                    (((long) buffer[offset + 2] & 0xff) << 8) |
                    (((long) buffer[offset + 3] & 0xff)));
        } else {
            return ((((long) buffer[offset + 3] & 0xff) << 24) |
                    (((long) buffer[offset + 2] & 0xff) << 16) |
                    (((long) buffer[offset + 1] & 0xff) << 8) |
                    (((long) buffer[offset] & 0xff)));
        }
    }
public static long toInt64(byte[] buffer, int offset, boolean bigEndian) {
        if (bigEndian) {
            return ((((long) buffer[offset]) << 56) |// 向左移位 右侧用0补齐 符号位不用 &0xff
                    (((long) buffer[offset + 1] & 0xff) << 48) |
                    (((long) buffer[offset + 2] & 0xff) << 40) |
                    (((long) buffer[offset + 3] & 0xff) << 32) |
                    (((long) buffer[offset + 4] & 0xff) << 24) |
                    (((long) buffer[offset + 5] & 0xff) << 16) |
                    (((long) buffer[offset + 6] & 0xff) << 8) |
                    (((long) buffer[offset + 7] & 0xff)));
        } else {
            return ((((long) buffer[offset + 7]) << 56) |
                    (((long) buffer[offset + 6] & 0xff) << 48) |
                    (((long) buffer[offset + 5] & 0xff) << 40) |
                    (((long) buffer[offset + 4] & 0xff) << 32) |
                    (((long) buffer[offset + 3] & 0xff) << 24) |
                    (((long) buffer[offset + 2] & 0xff) << 16) |
                    (((long) buffer[offset + 1] & 0xff) << 8) |
                    (((long) buffer[offset] & 0xff)));
        }

    }
    public static int toInt32(byte[] buffer, int offset, boolean bigEndian) {
        if (bigEndian) {
            return (((buffer[offset]) << 24) |// 转成有符号位 最高位 不用考虑符号位影响
                    ((buffer[offset + 1] & 0xff) << 16) |
                    ((buffer[offset + 2] & 0xff) << 8) |
                    ((buffer[offset + 3] & 0xff)));
        } else {
            return ((((buffer[offset + 3]) ) << 24) |
                    ((buffer[offset + 2] & 0xff) << 16) |
                    ((buffer[offset + 1] & 0xff) << 8) |
                    ((buffer[offset] & 0xff)));
        }

    }

根据前文 -9用大端表示为 0xfffffff7所以如下输出:

  byte[] bValue = new byte[]{(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xf7};// -9

 System.out.println("to unsight 32 "+toUnsignedInt32(bValue, 0, true));
 System.out.println("to int 32 " +toInt32(bValue, 0, true));

结果

to unsight 32 4294967287 to int 32 -9

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值