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

相关文章

Spring boot整合dubbo+zookeeper的详细过程

《Springboot整合dubbo+zookeeper的详细过程》本文讲解SpringBoot整合Dubbo与Zookeeper实现API、Provider、Consumer模式,包含依赖配置、... 目录Spring boot整合dubbo+zookeeper1.创建父工程2.父工程引入依赖3.创建ap

SpringBoot结合Docker进行容器化处理指南

《SpringBoot结合Docker进行容器化处理指南》在当今快速发展的软件工程领域,SpringBoot和Docker已经成为现代Java开发者的必备工具,本文将深入讲解如何将一个SpringBo... 目录前言一、为什么选择 Spring Bootjavascript + docker1. 快速部署与

Spring Boot spring-boot-maven-plugin 参数配置详解(最新推荐)

《SpringBootspring-boot-maven-plugin参数配置详解(最新推荐)》文章介绍了SpringBootMaven插件的5个核心目标(repackage、run、start... 目录一 spring-boot-maven-plugin 插件的5个Goals二 应用场景1 重新打包应用

SpringBoot+EasyExcel实现自定义复杂样式导入导出

《SpringBoot+EasyExcel实现自定义复杂样式导入导出》这篇文章主要为大家详细介绍了SpringBoot如何结果EasyExcel实现自定义复杂样式导入导出功能,文中的示例代码讲解详细,... 目录安装处理自定义导出复杂场景1、列不固定,动态列2、动态下拉3、自定义锁定行/列,添加密码4、合并

Spring Boot集成Druid实现数据源管理与监控的详细步骤

《SpringBoot集成Druid实现数据源管理与监控的详细步骤》本文介绍如何在SpringBoot项目中集成Druid数据库连接池,包括环境搭建、Maven依赖配置、SpringBoot配置文件... 目录1. 引言1.1 环境准备1.2 Druid介绍2. 配置Druid连接池3. 查看Druid监控

Java中读取YAML文件配置信息常见问题及解决方法

《Java中读取YAML文件配置信息常见问题及解决方法》:本文主要介绍Java中读取YAML文件配置信息常见问题及解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要... 目录1 使用Spring Boot的@ConfigurationProperties2. 使用@Valu

创建Java keystore文件的完整指南及详细步骤

《创建Javakeystore文件的完整指南及详细步骤》本文详解Java中keystore的创建与配置,涵盖私钥管理、自签名与CA证书生成、SSL/TLS应用,强调安全存储及验证机制,确保通信加密和... 目录1. 秘密键(私钥)的理解与管理私钥的定义与重要性私钥的管理策略私钥的生成与存储2. 证书的创建与

浅析Spring如何控制Bean的加载顺序

《浅析Spring如何控制Bean的加载顺序》在大多数情况下,我们不需要手动控制Bean的加载顺序,因为Spring的IoC容器足够智能,但在某些特殊场景下,这种隐式的依赖关系可能不存在,下面我们就来... 目录核心原则:依赖驱动加载手动控制 Bean 加载顺序的方法方法 1:使用@DependsOn(最直

SpringBoot中如何使用Assert进行断言校验

《SpringBoot中如何使用Assert进行断言校验》Java提供了内置的assert机制,而Spring框架也提供了更强大的Assert工具类来帮助开发者进行参数校验和状态检查,下... 目录前言一、Java 原生assert简介1.1 使用方式1.2 示例代码1.3 优缺点分析二、Spring Fr

java使用protobuf-maven-plugin的插件编译proto文件详解

《java使用protobuf-maven-plugin的插件编译proto文件详解》:本文主要介绍java使用protobuf-maven-plugin的插件编译proto文件,具有很好的参考价... 目录protobuf文件作为数据传输和存储的协议主要介绍在Java使用maven编译proto文件的插件