Java移位运算原理解析
位运算
引言
最近在为了面试做准备,一直想多了解一些Java底层的运行机制,偶然看到了移位运算符,虽然说在Java这种OOP语言中用的可能没有面向过程语言中用的频繁,但是技多不压身嘛,所以写下了这篇博文将知识分享给大家。
位运算的优点是什么?
位运算的优点就是大大的提高了程序的效率。
什么是位运算?
所有的数据在计算机内存中都是以二进制的形式存在的,通过使用位运算符可以直接操纵二进制数据,而且我们正常的运算符也是通过计算机通过位运算调用栈来实现,所以通过位运算符操纵数据可以节约计算机的内存,同时也大大的提高了程序的效率。
都有哪些位运算符
有符号左移 和 右移 <<
和 >>
无符号左移 和 右移 <<<
和 >>>
有符号左移/右移详解
让我们看一下此处的代码
int a = 12;
int b = a >> 2; //有符号右移两位
System.out.println(b);
这里的输出结果是3,原理剖析如下。
移位运算符原理剖析
要想知道这个运算结果是怎么得来的,我们就要探究计算机存储数据的终极奥秘,原码、反码和补码。
原码为原数据的直接二进制数的表示。
例如byte
类型数据12
的原码是00001100
例如byte
类型数据-12
的原码是10001100
反码,正数的反码和原码相同,负数的反码是除符号位(二进制的第一位表示符号为,0为正1为负)原码按位取反得来的。
例如byte
类型数据12
的反码是00001100
例如byte
类型数据-12
的反码是11110011
补码,正数的补码和原码相同,负数的补码是反码+1,补码进行反码+1就是原码。
例如byte
类型数据12
的补码是00001100
例如byte
类型数据-12
的补码是11110100
总结,计算机存储数据用的是补码,计算的任何结果都是补码,需要转换骋原码才可以进行进制换算。
我们例子中给出的变量a
是int
类型的,在我第一篇博文《浅谈Java变量和数据类型》
中提到,int
类型的数据占用32位储存空间,所以我们变量a
的原码一定是32位的二进制数据。
00000000 00000000 00000000 00001100
//12的原码
由于正数的原码、反码、补码都相同,所以可得12的补码是
00000000 00000000 00000000 0000100
//12的补码
有符号右移N位,在符号位后自动补N个0(正数)或1(负数 )
0 ==00== 00000000 00000000 00000000 00011
//左边的两个0为右移后补全的0
再将此二进制数据转换成十进制的数据,就是我们上面的运算结果3
如果说负数右移两位呢?代码如下
int a = -12;
int b = -12 >> 2;
System.out.println(b);
结果是==-3==
10000000 00000000 00000000 00001100
//第一位是符号位。-12的原码
11111111 11111111 11111111 11110011
//-12的反码
反码+1求补码
11111111 11111111 11111111 11110100
//-12的补码
开始右移,右边末端去掉n位数,因为是有符号,所以默认在符号位后补2个1
11 11111111 11111111 11111111 111101
//移动后的-12补码
10 00000000 00000000 00000000 000010
//反码
反码+1
10 00000000 00000000 00000000 000011
//结果 -3
左移原理与右移原理相同啦
无符号左移/右移
无符号的移位运算符与有符号的移位运算符原理上大体相同啦,区别就是在有符号移位运算中,我们所补全的数字是取决于符号位(符号位是0就补0,是1就补1),但是在无符号的移位运算中,不管是正数还是负数,都补0。
例如
10000000 00000000 00000000 00001100
//第一位是符号位。-12的原码
11111111 11111111 11111111 11110011
//-12的反码
反码+1求补码
11111111 11111111 11111111 11110100
//-12的补码
开始右移,右边末端去掉n位数,因为是有符号,所以默认在符号位后补2个1
00 11111111 11111111 11111111 111101
//移动后的-12补码,同时也是原码,因为是正数
因为第一位是0,表示正数,正数的原码、反码、补码是完全相同的,所以结果用计算机换算得来是1073741821
有的同学可能在这里就产生疑问了
说,你这个-12的补码移位之后不是应该换回原码吗?但是请各位看仔细,因为我是无符号右移,所以我前面默认添了0,这个数就由负数变成正数了,而正数的原码反码补码都相同,所以说结果不用继续换算啦。
左移原理也相同哈
结束语
写于2019.3.19,中软基地。
最大的感受是经过这一系列的培训,对于Java底层原理兴趣越来越浓厚。