Java中byte转int类型为什么要amp;amp;amp;amp;amp;0xFF?

2024-01-26 03:10
文章标签 java 类型 int byte amp 0xff

本文主要是介绍Java中byte转int类型为什么要amp;amp;amp;amp;amp;0xFF?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

今天查看代码,翻到MD5加密代码,如下:

public String getMD5(String source) throws Exception {StringBuilder sb = new StringBuilder();byte[] output = MessageDigest.getInstance("MD5").digest(source.getBytes("utf-8"));for (int i = 0; i < output.length; i++) {int c = output[i] & 0xFF;if (c < 16) {sb.append("0");}sb.append(Integer.toHexString(c));}return sb.toString();}int c = output[i] & 0xFF;if (c < 16) {sb.append("0");}sb.append(Integer.toHexString(c));}return sb.toString();}

为什么要&0xFF ?

 

各种查资料,了解到,计算机中存储都是利用二进制的补码存储的,存储数据机制:正数存储的二进制原码,负数存储的是二进制的补码。  补码是负数的绝对值反码加1。

计算机为什么用补码存储数据,当我们明白这个问题后,就可以去理解另一个衍生问题——数据溢出

首先我们看一段数据溢出的Java代码:

/*char是无符号数,16位存储,表示范围是0~2^16-1(即0~65535)*/
char ch = Character.MAX_VALUE; // ch为65535
ch += (char) 1; // 加1后,引起数据溢出,则ch为0/*int是有符号数,32位存储,表示范围是-2^31~2^31-1(即-2147483648~2147483647)*/
int i = Integer.MAX_VALUE; // i为2147483647
i += 1; // 加1后,引起数据溢出,则i为-2147483648

为什么要使用原码,反码,补码 ?

 

【加法器】计算机只有加法器没有减法器(计算机只能识别0和1,也就是高点平和低电平,最底层的运算就是0和1的各种数学运算,也就是二进制运算,又因为组成最基本的输入输出的逻辑门实现二进制加法最简单,所以所有的数学运算都是建立在加法基础上再通添加各种逻辑门来改进的),两个数的减法运算会被计算机转换为加法运算。目的:为了简化计算机基本运算电路,使加减法都只需要用过加法电路实现,也就是让减去一个整数或加上一个负数这样的运算可以用加上一个整数来代替。于是改变负数存储的形式,存储成一种可以直接当成正数来相加的形式,这种形式就是补码。

模数】模数从物理意义上讲是某种计量器的容量。经常举的一个例子就是钟表,其模数是12,即每到12就重新从0开始,数学上叫取模或者求余(mod)。java中用%表示求余,例如 14 % 12  = 2。如果此时的正确时间为6点,而你的手表指向的是8点,要把表对准,有两种方法:一是把表逆时针拨两小时;二是把表顺时针拨10小时。

即8-2=6,(8+10)%12 =6

也就是说在此模数系统里面有8-2=8+10

这是因为2跟10对模数12互为补数。因此有以下结论:在模数系统,A-B或A+(-B)等价于A+[B补],

即8-2或8+(-2)=8+10

我们把10叫做-2在模12下的补码。我们把补码表示法(two's complement representation)所表示的四位存储单

元,按照从0000到1111递增,均匀分布在时钟的表盘上。如下图所示:

顺时针方向为加法,逆时针方向为减法

模为2^n:在1111处顺时针拨一格,就到了0000。用数学的方式,即1111+1=10000,进位舍弃则结果为0000。那么四位存储的模就是10000(2^4),这样用补码来表示负数就可以将加减法统一成加法来运算,简化了运算的复杂的程度。

减法转换为加法:3-1=3+(-1)=0011+1111=0010,到这里有人就有疑问了,0011+1111明明等于10010,怎么会是0010呢?还记得之前提过的最高位进位舍弃嘛,因此对于4位存储来说,进位舍弃就是0010=2。

数据溢出:当0111(7)加1时,按照我们人的思维来说,应该结果为8,但是对于机器来说则不是,因为0111(7)是四位存储所能表示的最大符号数,所以它是无法表示01000(8)的,这个时候我们就说数据溢出了。机器的思考方式显然和我们人脑不一样,机器按照上面环形图的方式,由于0111(7)加1时顺时针造成的数据溢出,那么我们可以把机器的操作想象成在0111(7)处顺时针拨了一格,我们再去对照下环形图发现这个时候指向了1000(-8)。这个过程想象成拨时针就OK了,对于1000(-8)减1也是同样的道理。

 

至此,我们完全可以总结一下:

1、计算机只有加法器没有减法器,两个数的减法运算会被计算机转换为加法运算,而补码正好能够解决减法转换为加法的问题。

2、防止机器发生零重码,同时解决了原码和反码不能表示-8的问题,这样极大的简化了计算机的硬件设计。

3、以循环的方式解决数据溢出的问题

 

现在我们回归到正题:

1byte = 1字节 = 8bit ,1int = 4字节 = 32bit

比如-12,[0000 1100]原  ,[1111 0011]反  ,[1111 0100]补码

byte-->int,就是8位变成32位

系统检测到byte转int进行运算时,按符号位补位,高位全部补1:  1111 1111 1111 1111 1111 1111 1111 0100

0xFF的二进制表示:1111 1111 ,高位全部补0: 0000 0000 0000 0000 0000 0000 1111 1111

-12的补码与0xFF进行与(&)操作最后就是:  0000 0000 0000 0000 0000 0000 1111 0100(十进制是244)

 

当从数字类型扩展到较宽的类型时,补零扩展还是补符号位扩展。由于Java只有有符号数,当byte扩展到short, int时,即正数都一样,因为符号位是0,所以无论如何都是补零扩展,但负数补零扩展和按符号位扩展结果完全不同。

byte[] a = new byte[10];
a[0]= -127;
int c = a[0]&0xff;

补符号数,原数值不变。

原码:1111 1111    补码:1000 0001  转成int类型,高位补1: 1111 1111 1111 1111 1111 1111 1000 0001 这个32位二进制补码表示的也是-127,显然这个两个补码表示的十进制数字依然是相同。

补零时,相当于把有符号数看成无符号数。比如-127 = 0x81,看成无符号数就是129

原码:1111 1111  补码:1000 0001 转成int类型,高位补0 : 0000 0000 0000 0000 0000 0000 1000 0001 这个32位二进制补码表示的是129

也就是说,byte转int类型时,自动转型是按符号位扩展的,这样能保证十进制的数值不会变,而&0xFF是补0扩展,这样能保证二进制存储的一致性,但是十进制数值已经发生变化了。而正数可以说是既按符号位扩展,又是补0扩展,所以在二进制存储和十进制数值上都能保证一致。

 

参考:

https://www.jianshu.com/p/63cc96758d20

http://www.cnblogs.com/think-in-java/p/5527389.html

https://blog.csdn.net/xmc281141947/article/details/74740061

 

 

 

这篇关于Java中byte转int类型为什么要amp;amp;amp;amp;amp;0xFF?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/645463

相关文章

SpringBoot集成XXL-JOB实现任务管理全流程

《SpringBoot集成XXL-JOB实现任务管理全流程》XXL-JOB是一款轻量级分布式任务调度平台,功能丰富、界面简洁、易于扩展,本文介绍如何通过SpringBoot项目,使用RestTempl... 目录一、前言二、项目结构简述三、Maven 依赖四、Controller 代码详解五、Service

Java中HashMap的用法详细介绍

《Java中HashMap的用法详细介绍》JavaHashMap是一种高效的数据结构,用于存储键值对,它是基于哈希表实现的,提供快速的插入、删除和查找操作,:本文主要介绍Java中HashMap... 目录一.HashMap1.基本概念2.底层数据结构:3.HashCode和equals方法为什么重写Has

Java 正则表达式的使用实战案例

《Java正则表达式的使用实战案例》本文详细介绍了Java正则表达式的使用方法,涵盖语法细节、核心类方法、高级特性及实战案例,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要... 目录一、正则表达式语法详解1. 基础字符匹配2. 字符类([]定义)3. 量词(控制匹配次数)4. 边

Java Scanner类解析与实战教程

《JavaScanner类解析与实战教程》JavaScanner类(java.util包)是文本输入解析工具,支持基本类型和字符串读取,基于Readable接口与正则分隔符实现,适用于控制台、文件输... 目录一、核心设计与工作原理1.底层依赖2.解析机制A.核心逻辑基于分隔符(delimiter)和模式匹

Java中的stream流分组示例详解

《Java中的stream流分组示例详解》Java8StreamAPI以函数式风格处理集合数据,支持分组、统计等操作,可按单/多字段分组,使用String、Map.Entry或Java16record... 目录什么是stream流1、根据某个字段分组2、按多个字段分组(组合分组)1、方法一:使用 Stri

Java+AI驱动实现PDF文件数据提取与解析

《Java+AI驱动实现PDF文件数据提取与解析》本文将和大家分享一套基于AI的体检报告智能评估方案,详细介绍从PDF上传、内容提取到AI分析、数据存储的全流程自动化实现方法,感兴趣的可以了解下... 目录一、核心流程:从上传到评估的完整链路二、第一步:解析 PDF,提取体检报告内容1. 引入依赖2. 封装

使用Spring Cache本地缓存示例代码

《使用SpringCache本地缓存示例代码》缓存是提高应用程序性能的重要手段,通过将频繁访问的数据存储在内存中,可以减少数据库访问次数,从而加速数据读取,:本文主要介绍使用SpringCac... 目录一、Spring Cache简介核心特点:二、基础配置1. 添加依赖2. 启用缓存3. 缓存配置方案方案

Java实现复杂查询优化的7个技巧小结

《Java实现复杂查询优化的7个技巧小结》在Java项目中,复杂查询是开发者面临的“硬骨头”,本文将通过7个实战技巧,结合代码示例和性能对比,手把手教你如何让复杂查询变得优雅,大家可以根据需求进行选择... 目录一、复杂查询的痛点:为何你的代码“又臭又长”1.1冗余变量与中间状态1.2重复查询与性能陷阱1.

深度剖析SpringBoot日志性能提升的原因与解决

《深度剖析SpringBoot日志性能提升的原因与解决》日志记录本该是辅助工具,却为何成了性能瓶颈,SpringBoot如何用代码彻底破解日志导致的高延迟问题,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言第一章:日志性能陷阱的底层原理1.1 日志级别的“双刃剑”效应1.2 同步日志的“吞吐量杀手”

Spring创建Bean的八种主要方式详解

《Spring创建Bean的八种主要方式详解》Spring(尤其是SpringBoot)提供了多种方式来让容器创建和管理Bean,@Component、@Configuration+@Bean、@En... 目录引言一、Spring 创建 Bean 的 8 种主要方式1. @Component 及其衍生注解