byte如何正确转int

背景

项目中需要接收到的字节数组中的某一个字节(16进制) 转成 int , 但是这个字节是无符号的,一开始直接使用byte强转int(使用java语言),没想到遇到了大坑,同时也触及到了之前没有注意的一些关于java底层类型存储类型的设计,下面详细说明

byte转int引发的线上问题

项目(java语言)中会对接收到的字节数组进行解析,其中某一位的字节-16进制表示,表示一个数字。协议文档里也没写是否是有符号还是无符号的,只写了16进制表示的字节。因此想当然写下了如下的方法

ini 复制代码
byte original = bytes[14];
int actValue = (int) original; 

但是某一天 bytes[14] 突然传过来一个 0xA5 , 结果拿到的actValue 却是 -91 ,而原本数据源端想传过来的是 165, 从而导致了一次事故。事后进行分析复盘才发现,一个小小的byte转int,在java中涉及到蛮多之前不曾注意到的细节,这种对细节的处理,也让我对网络编程序列化等有了更多自己的理解

如何正确处理byte转int

一般而言,java中的基本数据类型都是分为有符号和无符号之分的,

比如对于0xA5这个字节来说,如何要解析成

  • 有符号的byte , 则 0xA5 最高位就要被看做符号位,因此真实数值就是 -91

    • 方式一: 原码:10100101 取反:01011010 (按位取反) 加1: 01011011 (加1得到绝对值) ,计算得到91 ,因为是负数得到-91

    • 方式二:

        1. 对整个二进制数(1010 0101减 11010 0101 - 1 = 1010 0100 2. 对结果进行按位取反1010 0100 -> 0101 1011 3. 将 0101 1011 转换为十进制: * 0*128 + 1*64 + 0*32 + 1*16 + 1*8 + 0*4 + 1*2 + 1*1 * = 64 + 16 + 8 + 2 + 1 = 91 4. 因为原数是负数,所以结果是 -91
  • 对于无符号的byte , 则 0xA5 全都看成数值,则是 165

因为java本身默认的就是有符号的,所以这种方式就不说了,下面看下如何把byte转成无符号的数值,因为java默认有符号,所以165超出了byte能承接的范围,此时只能通过short或者int来承接了。正确的方式如下

ini 复制代码
byte original = bytes[14];
int actValue = original & 0xFF ; 

就能得到正确的值165了。但是仔细分析下这个代码,不禁有几个疑问

为什么和 0xFF 做与运行就可以了,我们知道0xFF 也就是 8位1 ,这样可以保留 original 字节所有位的1,但是这和 original原本没有任何区别呀,为什么最后就能正确处理了呢

这里发生了什么呢?

b 先被符号扩展为 0xFFFFFF A5(-91)

0xFF0x000000FF

按位与操作:

scss 复制代码
11111111 11111111 11111111 10100101  (b扩展后)
00000000 00000000 00000000 11111111  (0xFF)
────────────────────────────────────
00000000 00000000 00000000 10100101  (结果:165)

问题又来了,为什么 0xFF 展开就变成了 00000000 00000000 00000000 11111111 ,而不是高位补1 而是补 0 呢 ?

这涉及到Java中字面量 类型自动类型提升的机制。

关键区别: 字面量 vs 变量

  1. 0xFF 是 int 类型字面量
arduino 复制代码
0xFF  // 这是一个 int 类型的字面量,值为 255

在Java中:

  • 整数字面量(如 0xFF255)默认是 int 类型
  • int 是32位有符号整数
  • 0xFF = 255,在32位中表示为:00000000 00000000 00000000 11111111
  1. byte变量需要符号扩展
ini 复制代码
byte b = (byte)0xA5;            // b 是 byte 类型变量
int result = b & 0xFF;

当进行 b & 0xFF 运算时:

第一步:操作数提升

  • b(byte)被符号扩展为 int:11111111 11111111 11111111 10100101
  • 0xFF(int字面量)保持:00000000 00000000 00000000 11111111

第二步:按位与运算

scss 复制代码
11111111 11111111 11111111 10100101  (b扩展后)
00000000 00000000 00000000 11111111  (0xFF)
────────────────────────────────────
00000000 00000000 00000000 10100101  (结果)

为什么0xFF不需要符号扩展?

因为 0xFF(255)对于 int 类型来说是正数

csharp 复制代码
int value = 0xFF;  // 255,正数
System.out.println(Integer.toBinaryString(0xFF)); 
// 输出: 11111111 (实际是32位,高位都是0) 

总结

本文通过一次byte转int来说明java中数据类型的底层存储方式,以及在转换时java在自动变量提升时所做的一些转换,通过探究背后的工作从而对数据的底层存储有了更深入的认识和了解

相关推荐
aiopencode17 分钟前
移动端网页调试实战,触摸事件穿透与点击冲突问题的定位与优化
后端
菜鸟Debug19 分钟前
🚀 Redisson 分布式锁源码解析:从加锁到解锁的完整流程
后端
数据智能老司机19 分钟前
图算法趣味学——最大流算法
数据结构·算法·云计算
该用户已不存在19 分钟前
2025年,Javascript后端应该用 Bun、Node.js 还是 Deno?
javascript·后端
哈基米喜欢哈哈哈1 小时前
Cookies和Sessions
网络·后端·计算机网络
嘟嘟MD1 小时前
程序员副业 | 2025年7月复盘
后端·程序员·创业
秋难降1 小时前
【数据结构与算法】———深度优先:“死磕 + 回头” 的艺术
数据结构·python·算法
AAA修煤气灶刘哥1 小时前
手把手教你Mybatis-Plus :小白看完都能会,看完还不回找我,我给你补个蛋
java·后端
一灯架构1 小时前
Mermaid 教程大全:10 分钟上手代码式画图
后端·markdown
数据智能老司机1 小时前
图算法趣味学——图着色
数据结构·算法·云计算