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应用中出现的Full GC问题的场景与解决

《SpringBoot应用中出现的FullGC问题的场景与解决》这篇文章主要为大家详细介绍了SpringBoot应用中出现的FullGC问题的场景与解决方法,文中的示例代码讲解详细,感兴趣的小伙伴可... 目录Full GC的原理与触发条件原理触发条件对Spring Boot应用的影响示例代码优化建议结论F

springboot项目中常用的工具类和api详解

《springboot项目中常用的工具类和api详解》在SpringBoot项目中,开发者通常会依赖一些工具类和API来简化开发、提高效率,以下是一些常用的工具类及其典型应用场景,涵盖Spring原生... 目录1. Spring Framework 自带工具类(1) StringUtils(2) Coll

SpringBoot条件注解核心作用与使用场景详解

《SpringBoot条件注解核心作用与使用场景详解》SpringBoot的条件注解为开发者提供了强大的动态配置能力,理解其原理和适用场景是构建灵活、可扩展应用的关键,本文将系统梳理所有常用的条件注... 目录引言一、条件注解的核心机制二、SpringBoot内置条件注解详解1、@ConditionalOn

通过Spring层面进行事务回滚的实现

《通过Spring层面进行事务回滚的实现》本文主要介绍了通过Spring层面进行事务回滚的实现,包括声明式事务和编程式事务,具有一定的参考价值,感兴趣的可以了解一下... 目录声明式事务回滚:1. 基础注解配置2. 指定回滚异常类型3. ​不回滚特殊场景编程式事务回滚:1. ​使用 TransactionT

MySQL 中查询 VARCHAR 类型 JSON 数据的问题记录

《MySQL中查询VARCHAR类型JSON数据的问题记录》在数据库设计中,有时我们会将JSON数据存储在VARCHAR或TEXT类型字段中,本文将详细介绍如何在MySQL中有效查询存储为V... 目录一、问题背景二、mysql jsON 函数2.1 常用 JSON 函数三、查询示例3.1 基本查询3.2

Spring LDAP目录服务的使用示例

《SpringLDAP目录服务的使用示例》本文主要介绍了SpringLDAP目录服务的使用示例... 目录引言一、Spring LDAP基础二、LdapTemplate详解三、LDAP对象映射四、基本LDAP操作4.1 查询操作4.2 添加操作4.3 修改操作4.4 删除操作五、认证与授权六、高级特性与最佳

Spring Shell 命令行实现交互式Shell应用开发

《SpringShell命令行实现交互式Shell应用开发》本文主要介绍了SpringShell命令行实现交互式Shell应用开发,能够帮助开发者快速构建功能丰富的命令行应用程序,具有一定的参考价... 目录引言一、Spring Shell概述二、创建命令类三、命令参数处理四、命令分组与帮助系统五、自定义S

SpringSecurity JWT基于令牌的无状态认证实现

《SpringSecurityJWT基于令牌的无状态认证实现》SpringSecurity中实现基于JWT的无状态认证是一种常见的做法,本文就来介绍一下SpringSecurityJWT基于令牌的无... 目录引言一、JWT基本原理与结构二、Spring Security JWT依赖配置三、JWT令牌生成与

Java中Date、LocalDate、LocalDateTime、LocalTime、时间戳之间的相互转换代码

《Java中Date、LocalDate、LocalDateTime、LocalTime、时间戳之间的相互转换代码》:本文主要介绍Java中日期时间转换的多种方法,包括将Date转换为LocalD... 目录一、Date转LocalDateTime二、Date转LocalDate三、LocalDateTim

如何配置Spring Boot中的Jackson序列化

《如何配置SpringBoot中的Jackson序列化》在开发基于SpringBoot的应用程序时,Jackson是默认的JSON序列化和反序列化工具,本文将详细介绍如何在SpringBoot中配置... 目录配置Spring Boot中的Jackson序列化1. 为什么需要自定义Jackson配置?2.