详解 & 0xff 的作用

占用字节数 & 取值范围

Java一共有8种基本数据类型(原始数据类型):

类型            存储要求               范围(包含)                默认值              包装类
int           4字节(32位)          -2^31~ 2^31-1                  0               Integer
short         2字节(16位)          -215~215-1                     0               Short
long          8字节(64位)          -2^63~2^63-1                   0               Long
byte          1字节(8位)           -2^7~2^7-1                     0               Byte
float         4字节(32位)          -3.4e+38 ~ 3.4e+38            0.0f             Float
double        8字节(64位)          -1.7e+308 ~ 1.7e+308           0               Double
char          2字节(16位)          u0000~uFFFF(‘’~‘?’)         ‘0’              Character 
boolean       1/8字节(1位)          true, false                  FALSE             Boolean

对于float与double类型:

内存结构:

float类型: 内存中共占4个字节,32bit位,其中bit位从高到低,依次是1位符号位、8位指数位、23位尾数位;
double类型:内存中共占8字节,64bit位,其中bit位从高到低,依次是1位符号位、11位指数位、52位尾数位;

大端模式与小端模式

大端模式是指数据的低位保存在内存的高地址中,而数据的高位保存在内存的低地址中.
小端模式是指数据的低位保存在内存的低地址中,而数据的高位保存在内存的高地址中。

大小端模式的由来

在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8bit。但是在C语言中除了8bit的char之外,还有16bit的short型,32bit的long型(要看具体的编译器)。

另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如果将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。

大小端在内存中的存放方式举例

例如:

a) 16bit宽的数0x1234
1.Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:

1 内存地址
2 0x4000
3 0x4001
4 存放内容
5 0x34
6 0x12

2.Big-endian模式CPU内存中的存放方式则为:

1 内存地址
2 0x4000
3 0x4001
4 存放内容
5 0x12
6 0x34

jvm使用的是大端

 原码、反码、补码知识

源码

源码就是数字对应的二进制表示。

负数的源码 = 正数的源码取反再加1

反码

正数的反码就是源码自身。

负数的反码就是:保持源码符号位不变,其余各个位取反。

补码

正数的补码就是源码自身。

负数的补码就是: 反码基础上加1

如何根据补码计算源码:

  1. 最高位如果是0,即为正数,其补码即为源码;
  2. 最高位如果是1,即为负数,对此补码再次计算补码,结果即为源码(即:补码的补码即为源码)。

 

首先我们要都知道, &表示按位与,只有两个位同时为1,才能得到1, 0x代表16进制数,0xff表示的数二进制1111 1111 占一个字节.和其进行&操作的数,最低8位,不会发生变化.

1. 只是为了取得低八位

通常配合移位操作符>>使用

例如:java socket通信中基于长度的成帧方法中,如果发送的信息长度小于65535字节,长度信息的字节定义为两个字节长度。这时候将两个字节长的长度信息,

以Big-Endian的方式写到内存中

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


例如,有个数字 0x1234,如果只想将低8位写入到内存中 0x1234&0xff
0x1234 表示为二进制 0001001000110100

0xff 表示为二进制 11111111

两个数做与操作,显然将0xff补充到16位,就是高位补0

此时0xff 为 0000000011111111

与操作 1&0 =0 1&1 =1 这样 0x1234只能保留低八位的数 0000000000110100 也就是 0x34

2. 保证补码的一致性

我们只关心二进制的机器数而不关注十进制的值,那么byte &0xff只是对其最低8位的复制,通常配合逻辑或 ‘’|’'使用,达到字节的拼接,但不保证其十进制真值不变

1 public static void main(String[] args) {
2     byte b = -127;//10000001
3     int a = b;
4     System.out.println(a);
5     a = b&0xff;
6     System.out.println(a);
7 }//输出结果-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,

这样能保证补码的一致性,当然由于符号位发生变化,表示的十进制数就会变了

1 1111 1111 1111 1111 1111 1111 1000 0001
2 
3 &
4 
5 0000 0000 0000 0000 0000 0000 1111 1111
6 
7 结果是
8 
9 0000 0000 0000 0000 0000 0000 1000 0001

和原来的补码 一致,但是显然符号位变化了,表示的十进制数发生变化,变为129

结论:

java中基本类型从小扩展到大的数据类型时候,正数因为符号位是0,无论如何都是补零扩展,但是负数补零扩展和补符号位扩展完全不同,

负数补符号位扩展,保证十进制数不变

例如 byte>>>int -127自动按照补符号位扩展,在高24位补符号位1,表示的十进制数不变

补零扩展,保证补码的一致性,但是表示的十进制发生变化

例如,本例中byte提升为int,&0xff的操作

posted @ 2020-06-24 10:34  飘零枫叶  阅读(4384)  评论(0编辑  收藏  举报