Java中byte与short、int、long互转(包含大端,小端)

1.转换函数

大端:高位字节排放在内存的低地址端,低位字节排放在内存的高地址端
小端:低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。

/**
     * 将int转为高字节在前,低字节在后的byte数组(大端)
     * @param n int
     * @return byte[]
     */
    public static byte[] intToByteBig(int n) {
        byte[] b = new byte[4];
        b[3] = (byte) (n & 0xff);
        b[2] = (byte) (n >> 8 & 0xff);
        b[1] = (byte) (n >> 16 & 0xff);
        b[0] = (byte) (n >> 24 & 0xff);
        return b;
    }
 /**
  * 将int转为低字节在前,高字节在后的byte数组(小端)
  * @param n int
  * @return byte[]
  */
    public static byte[] intToByteLittle(int n) {
        byte[] b = new byte[4];
        b[0] = (byte) (n & 0xff);
        b[1] = (byte) (n >> 8 & 0xff);
        b[2] = (byte) (n >> 16 & 0xff);
        b[3] = (byte) (n >> 24 & 0xff);
        return b;
    }
    /**
     * byte数组到int的转换(小端)
     * @param bytes
     * @return
     */
    public static int bytes2IntLittle(byte[] bytes )
    {
        int int1=bytes[0]&0xff;
        int int2=(bytes[1]&0xff)<<8;
        int int3=(bytes[2]&0xff)<<16;
        int int4=(bytes[3]&0xff)<<24;

        return int1|int2|int3|int4;
    }
    /**
     * byte数组到int的转换(大端)
     * @param bytes
     * @return
     */
    public static int bytes2IntBig(byte[] bytes )
    {
        int int1=bytes[3]&0xff;
        int int2=(bytes[2]&0xff)<<8;
        int int3=(bytes[1]&0xff)<<16;
        int int4=(bytes[0]&0xff)<<24;

        return int1|int2|int3|int4;
    }
    /**
     * 将short转为高字节在前,低字节在后的byte数组(大端)
     * @param n short
     * @return byte[]
     */
    public static byte[] shortToByteBig(short n) {
        byte[] b = new byte[2];
        b[1] = (byte) (n & 0xff);
        b[0] = (byte) (n >> 8 & 0xff);
        return b;
    }

    /**
     * 将short转为低字节在前,高字节在后的byte数组(小端)
     * @param n short
     * @return byte[]
     */
    public static byte[] shortToByteLittle(short n) {
        byte[] b = new byte[2];
        b[0] = (byte) (n & 0xff);
        b[1] = (byte) (n >> 8 & 0xff);
        return b;
    }
    /**
     *  读取小端byte数组为short
     * @param b
     * @return
     */
    public static short byteToShortLittle(byte[] b) {
        return (short) (((b[1] << 8) | b[0] & 0xff));
    }
    /**
     *  读取大端byte数组为short
     * @param b
     * @return
     */
    public static short byteToShortBig(byte[] b) {
        return (short) (((b[0] << 8) | b[1] & 0xff));
    }
    /**
     * long类型转byte[] (大端)
     * @param n
     * @return
     */
    public static byte[] longToBytesBig(long n) {
        byte[] b = new byte[8];
        b[7] = (byte) (n & 0xff);
        b[6] = (byte) (n >> 8  & 0xff);
        b[5] = (byte) (n >> 16 & 0xff);
        b[4] = (byte) (n >> 24 & 0xff);
        b[3] = (byte) (n >> 32 & 0xff);
        b[2] = (byte) (n >> 40 & 0xff);
        b[1] = (byte) (n >> 48 & 0xff);
        b[0] = (byte) (n >> 56 & 0xff);
        return b;
    }
    /**
     * long类型转byte[] (小端)
     * @param n
     * @return
     */
    public static byte[] longToBytesLittle(long n) {
        byte[] b = new byte[8];
        b[0] = (byte) (n & 0xff);
        b[1] = (byte) (n >> 8  & 0xff);
        b[2] = (byte) (n >> 16 & 0xff);
        b[3] = (byte) (n >> 24 & 0xff);
        b[4] = (byte) (n >> 32 & 0xff);
        b[5] = (byte) (n >> 40 & 0xff);
        b[6] = (byte) (n >> 48 & 0xff);
        b[7] = (byte) (n >> 56 & 0xff);
        return b;
    }
       /**
     * byte[]转long类型(小端)
     * @param array
     * @return
     */
    public static long bytesToLongLittle( byte[] array )
    {
        return ((((long) array[ 0] & 0xff) << 0)
                | (((long) array[ 1] & 0xff) << 8)
                | (((long) array[ 2] & 0xff) << 16)
                | (((long) array[ 3] & 0xff) << 24)
                | (((long) array[ 4] & 0xff) << 32)
                | (((long) array[ 5] & 0xff) << 40)
                | (((long) array[ 6] & 0xff) << 48)
                | (((long) array[ 7] & 0xff) << 56));
    }

    /**
     * byte[]转long类型(大端)
     * @param array
     * @return
     */
    public static long bytesToLongBig( byte[] array )
    {
        return ((((long) array[ 0] & 0xff) << 56)
                | (((long) array[ 1] & 0xff) << 48)
                | (((long) array[ 2] & 0xff) << 40)
                | (((long) array[ 3] & 0xff) << 32)
                | (((long) array[ 4] & 0xff) << 24)
                | (((long) array[ 5] & 0xff) << 16)
                | (((long) array[ 6] & 0xff) << 8)
                | (((long) array[ 7] & 0xff) << 0));
    }

2.Java的转换函数的简单理解

要想理解这个函数,关键点有3点:数据类型,位移操作符,& 0xff。
我们首先来看看 Java的数据类型。

2.1 Java的数据类型

image.png

范围:


image.png

这里要了解原码,反码,补码,与真值。

原码, 反码, 补码 详解
Java不论是负数还是正数在定义、存储、计算的过程中,都是用其补码。

來看一下int强转byte
int a = 165在计算机中存储的 数据(机器数:一个数在计算机中的二进制表示形式)是
0000 0000 0000 0000 0000 0000 1010 0101:
占4 个字节,32位,最高位符号位为0,真值为165

int强转为byte,机器数为
1010 0101 :
占1 个字节,8位,最高位的数据1变成了符号位,符号位变成1,真值为-37

image.png

int 强转为byte 丢失数据:丢失了原本符号位,并且丢失了一位数据,真值发生了改变。
byte存储范围 byte范围 -128 - 127,也存不了165这个真值,因此存储的是-35,但是机器数是不变的。

串口传输接口底层是按位(bit)发送的,上层是按byte发送和接收的,但协议为了方便描述,每个byte用十六进制数(0x00-0xFF)表示,范相当于十进制的0-255,而byte为八位且是有符号类型,相当于十进制的-128-127,明显0x8F-0xFF(128-255)是不能准确转换为byte的,因为165超过了byte的范围,不能直接赋值,只能强转。
byte b = (byte) 0x5A;

然后下层,取到该字节值后 该值进行符号扩展后 & 0xFF 转为165。
1111 1111 1111 1111 1111 1111 1010 0101
0000 0000 0000 0000 0000 0000 1111 1111


0000 0000 0000 0000 0000 0000 1010 0101


image.png

这样才保证了真值一致性。

符号扩展就是把所有高位重复符号位即可,0则全0,1则全1
如果是无符号数,想要扩展n位 则是在前面添加n位0

0000 0001扩展00 0000 0001

补码数的符号扩展

如果是补码数,想要扩展n位 看最高位是0还是1,是0则扩展0,是1则扩展1。

如果想要扩展2位,如下例:
扩展前: 1100 1111
扩展后: 11 1100 1111
扩展前: 0100 1111
扩展后:00 0100 1111

2.2 大端和小端

举一个例子,比如数字0x12 34 56 78在内存中的表示形式。

1)大端模式:Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。(其实大端模式才是我们直观上认为的模式,和字符串存储的模式差类似)即正序排列,高尾端;

低地址 --------------------> 高地址

0x0A | 0x0B | 0x0C | 0x0D

image.png

2)小端模式:Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。即逆序排列,低尾端;

低地址 --------------------> 高地址

0x0D | 0x0C | 0x0B | 0x0A


image.png

无论是小端模式还是大端模式,每个字节内部都是按顺序排列。

一般操作系统都是小端,而通讯协议是大端的。

实际中的例子

虽然很多时候,字节序的工作已由编译器完成了,但是在一些小的细节上,仍然需要去仔细揣摩考虑,尤其是在以太网通讯、MODBUS通讯、软件移植性方面。这里,举一个MODBUS通讯的例子。在MODBUS中,数据需要组织成数据报文,该报文中的数据都是大端模式,即低地址存高位,高地址存低位。假设有一16位缓冲区m_RegMW[256],因为是在x86平台上,所以内存中的数据为小端模式:m_RegMW[0].low、m_RegMW[0].high、m_RegMW[1].low、m_RegMW[1].high……
为了方便讨论,假设m_RegMW[0] = 0x3456; 在内存中为0x56、0x34。
现要将该数据发出,如果不进行数据转换直接发送,此时发送的数据为0x56,0x34。而Modbus是大端的,会将该数据解释为0x5634而非原数据0x3456,此时就会发生灾难性的错误。所以,在此之前,需要将小端数据转换成大端的,即进行高字节和低字节的交换,此时可以调用步骤五中的函数BigtoLittle16(m_RegMW[0]),之后再进行发送才可以得到正确的数据。

2.3 位移操作符,& 0xff 的作用

2.3.1 位移操作符

>>
表示右移,如果该数为正,则高位补0,若为负数,则高位补1。
>>
表示左移,形如 a<<b,将 a 的各二进制位整体向左移 b 位,高位溢出位移出,低位补 0。
>>>表示无符号右移。
正数的无符号右移:与右移规则一致。
负数的无符号右移:先将负数取反,得到反码,然后反码加1得到补码,补码再进行右移,这样得到的结果就是无符号右移的结果了。
为负的十进制整数
如:-64 >>> 5
①第一步:操作数的绝对值,转化为二进制
100 0000
②第二步:补码(操作数为负数)
1000 0000 0000 0000 0000 0000 0100 0000
③第三步:取反(符号位不变,其他位1、0相互转换),在第一位加上1
1111 1111 1111 1111 1111 1111 1100 0000
④第四步:右移5位
0000 0111 1111 1111 1111 1111 1111 1110
结果为:134217726

左移:低位补0
右移: 高位补0或1

2.3.2 & 0xff 的作用

  1. 取得低八位
    通常配合移位操作符>>使用

定义为两个字节长度。这时候将两个字节长的长度信息,以Big-Endian的方式写到内存中

out.write((message.length>>8)&0xff);//取高八位写入地址
out.write(message.length&0xff);//取低八位写入高地址中

例如,有个数字 0x1234,如果只想将低8位写入到内存中 0x1234&0xff
0x1234表示为二进制0001 0010 0011 0100
0xff 表示为二进制1111 1111
两个数做与操作,显然将0xff补充到16位,就是高位补0
此时0xff0000 0000 1111 1111

与操作 1&0 =0 1&1 =1 这样 0x1234只能保留低八位的数 0000 0000 0011 0100也就是0x34
2.保证机器数的一致性

public static void main(String[] args) {
        byte b = -127;//10000001
        int a =  b;
        System.out.println(a);
        a =  b&0xff;
        System.out.println(a);
    }//输出结果-127,129

b是8位的二进制数,在与上0xff(也就是 11111111),不就是其本身吗,输出在控制台结果为什么是129呢?

首先计算机内的存储都是按照补码存储的,-127补码表示为 1000 0001

int a = b;将byte 类型提升为int时候,b的补码提升为 32位,补码的高位补1,也就是

1111 1111 1111 1111 1111 1111 1000 0001

负数的补码转为原码,符号位不变,其他位取反,在加1,正数的补码,反码都是本身

结果是 1000 0000 0000 0000 0000 0000 0111 1111表示为十进制 也是 -127

也就是 当 byte -> int 能保证十进制数(真值)不变,但是有些时候比如文件流转为byte数组时候,我们不是关心的是十进制数(真值)有没有变,而是机器数有没有变,这时候需要&上0xff。

本例子中,将byte转为int 高24位必将补1,此时补码显然发生变化,在与上0xff,将高24重新置0,

这样能保证机器数不变,当然由于符号位发生变化,表示的十进制数(真值)就会变了

 1111 1111 1111 1111 1111 1111 1000 0001 
&
 0000 0000 0000 0000 0000 0000 1111 1111

结果是
0000 0000 0000 0000 0000 0000 1000 0001

结论:

java中基本类型从小扩展到大的数据类型时候,正数因为符号位是0,无论如何都是补零扩展,但是负数补零扩展和补符号位扩展完全不同,
负数补符号位扩展真值不变
例如 byte>>>int -127自动按照补符号位扩展,在高24位补符号位1,真值不变。
补零扩展,保证机器数的一致性,但是真值发生改变

2.3.3 若不使用&0xff,什么情况不能正确转换?什么时候能够正确转换?

Java int和byte数组互相转换时为什么要用到&0xff?
1.int转byte数组时不需要用到&0xff,因为int转byte时,系统会自动将溢出的位数忽略。
2.byte数组还原为int时,低24位截取出的3个byte符号位都为0时,不论是否使用&0xff都不会影响正常转换。

image.png

3.byte数组还原为int时,如果使用的int值拆分成的4个byte符号位中,低24位截取出的3个byte只要有一个符号位为1,只有使用&0xff才能正常转换。
image.png

参考链接:

详解大端模式和小端模式
大端模式和小端模式
java基本类型与byte字节数组的转换(包含大端,小端)

原码, 反码, 补码 详解
补码/反码、零扩展和符号位扩展(Zero extension and Sign extension)
笔记:扩展一个数字的位表示 无符号数的零扩展 补码数的符号扩展

byte为什么要与上0xff?
从强转 byte 说起
详解 & 0xff 的作用
Java int和byte数组互相转换时为什么要用到&0xff?

运算符>>和>>>有什么区别
位运算符之无符号右移(十进制整数)
java中的左移、右移、无符号右移

android串口通信接受自定义协议数据并解析问题

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容