动力节点首页 全国咨询热线:400-8080-105

绑定手机号,登录
手机号

验证码

微信登录
手机号登录
手机号

验证码

微信登录与注册
微信扫码登录与注册

扫码关注微信公众号完成登录与注册
手机号登录
首页 > 文章

Java移位运算符详解

05-19 11:08 1073浏览
举报 T字号
  • 大字
  • 中字
  • 小字

在阅读源码的过程中,我们经常会看到这些符号<< ,>>,>>>,这些符号在Java中叫移位运算符,在写代码的过程中,虽然我们基本上不会去写这些符号,但需要明白这些符号的运算原理,比如HashMap中有以下代码:

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;//左移
static final int hash(Object key) {
     int h;
     return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);//无符号右移
}

上段代码中就包含左移运算符<<,无符号右移运算符>>>。

移位操作是把数据看作二进制数,然后将其向左或向右移动若干位的运算。在Java中,移位运算符包含三种:<<左移运算符,>>带符号右移运算符,>>>无符号右移运算符。这三种操作符都只能作用于long,int,short,byte这四种基本整形类型上和char类型上。下面我们来详细介绍这三种移位运算符。

1.左移操作符<<

左移操作符<<是将数据转换为二进制后,向左移动若干位,高位丢弃,低位补零。

首先我们可以利用java中的方法获取一个数的二进制:Integer.toBinaryString(int val)。

然后我们看下面这个例子:

public static void main(String[] args) {
int a = 10;
System.out.println("左移前的二进制:"+Integer.toBinaryString(a));
a <<= 2;
System.out.println("左移后的二进制:"+Integer.toBinaryString(a));
System.out.println("左移后的十进制:"+a);
}

首先定义一个数,值为10,打印它的二进制(1010),然后进行左移操作2位。打印移位后的结果和二进制。

左移前的二进制:1010

左移后的二进制:101000

左移后的十进制:40

可以看出,将原来的二进制向左移动了两位,后面进行了补零。40=10 * 2 * 2。所以一次左移等于将这个数扩大了两倍。对于左移N位,就等于乘以2^n,如40=10*2^2。再来看一个负数的左移:

int b = -8;

System.out.println("左移前的二进制:" + Integer.toBinaryString(b));

b <<= 2;

System.out.println("左移后的二进制:" + Integer.toBinaryString(b));

System.out.println("左移后的十进制:" + b);

我们定义了一个负数(-8),打印出它的二进制,进行左移2位,左移后打印它的二进制,再将10进制打印出来查看。

左移前的二进制:11111111111111111111111111111000

左移后的二进制:11111111111111111111111111100000

左移后的十进制:-32

可以明显的看出二进制向左移动了两位,前面的位置丢弃,后面的位置补零。转换为10进制也符合我们之前的运算:-32 = -8 * 2 *2。

2.带符号右移操作符>>

刚才的左移中,它向左移动,高位进行了丢弃,低位进行补零。但是右移操作时有一个符号位,操作不当将造成答案与预期结果不同。

带符号右移就是在**向右移动若干位,低位进行丢弃,高位按照符号位进行填补。**对于正数做右移操作时,高位补充0;负数进行右移时,高位补充1。

再来用例子证明一下:

public static void main(String[] args) {
int a = 1024;
System.out.println("a右移前的二进制:" + Integer.toBinaryString(a));
a >>= 4;
System.out.println("a右移后的二进制:" + Integer.toBinaryString(a));
System.out.println("a右移后的十进制:"+a);
int b = -70336;
System.out.println("b右移前的二进制:" + Integer.toBinaryString(b));
b >>= 4;
System.out.println("b右移后的二进制:" + Integer.toBinaryString(b));
System.out.println("b右移后的十进制:"+b);
}

定义了两个变量,a=1024,然后向右移动4位。b=-70336也向右移动4位。分别将它们的移动前后二进制和十进制打印出来查看。

a右移前的二进制:10000000000

a右移后的二进制:1000000

a右移后的十进制:64

b右移前的二进制:11111111111111101110110101000000

b右移后的二进制:11111111111111111110111011010100

b右移后的十进制:-4396

a原来的二进制向右移动后,低位被丢弃,高位补充符号位也就是0。b原来的二进制向右移动后,低位被丢弃,高位补充符号位1。这也符号我们之前的运算规律:

1024 / 2^4^ =16 ;-70336/ 2^4^ = -4396。

3.无符号右移操作符>>>

刚才的带符号右移操作符,我们在向右移动时带着高位的符号,正数填充0,负数填充1。现在不带符号的右移操作符大体与右移操作符一致,只不过不再区分正负数,结果都是高位补零,低位丢弃。

再用例子来证明一下:

public static void main(String[] args) {
int a = 1024;
System.out.println("a右移前的二进制:" + Integer.toBinaryString(a));
a >>>= 4;
System.out.println("a右移后的二进制:" + Integer.toBinaryString(a));
System.out.println("a右移后的十进制:"+a);
int b = -70336;
System.out.println("b右移前的二进制:" + Integer.toBinaryString(b));
b >>>= 4;
System.out.println("b右移后的二进制:" + Integer.toBinaryString(b));
System.out.println("b右移后的十进制:"+b);
}

还是刚才带符号右移的例子:这次我们仅仅把操作符换成无符号的右移操作符。

按照定义,其实在正数时不会有变化,因为在带符号的右移中正数也是高位补零。只不过当值为负数时会有变化,让我们看一下输出是不是符合猜想。

a右移前的二进制:10000000000

a右移后的二进制:1000000

a右移后的十进制:64

b右移前的二进制:11111111111111101110110101000000

b右移后的二进制:1111111111111110111011010100

b右移后的十进制:268431060

确实正数没有变化,验证了我们的猜想。然后是负数,这次向右移动时高位进行了补零,低位丢弃。改变后的数值不再符合我们之前的规律。

在无符号右移中,当值为正数时,依然符合之前的规律移动一位相当于除以2。但是当值为负数时不再符合规律。

当移位的位数超过数值所占用的位数会怎么样?

这个问题很有意思,我们刚刚都仅仅移动了2位或者4位,如果我们超过了int的位数也就是32位后会怎么样?我们如果对一个正数左移32位,低位补零补充了32次就变成0了,就如同下面代码所写的一样,最终a的结果会是什么。会变成0吗?

public static void main(String[] args) {
int a = 10;
a <<= 32;
System.out.println(a);
}

经过我们运行后发现a的结果最终没变还是10。我们如果改成左移33位,它的结果会变成20。那么它的运算规律会不会是当超过位数后仅仅移动对位数的余数呢?比如对int做操作,它实际是运算 位数%32次。

经过多次试验发现答案确实就是这个猜想,当对int类型处理时,右移x位的运算为x%32位。

对其他类型也是一样吗?

我们刚才都是用的int类型,那么对于byte,short,char,long都一样吗?

事实上,Java在对byte,short,char这三种类型进行移位操作前,会将其先转型为int类型,然后再进行位操作。由于我们有进行了重新赋值将其赋值为原来的byte类型,所以又进行了从int到byte的先下转型,也就是截断。我们对上面的例子进行一下修改可以更直观的发现运行过程:

public static void main(String[] args) {
byte b = -1;
System.out.println("操作前十进制:"+b);
System.out.println("操作前二进制:"+Integer.toBinaryString(b));
System.out.println("进行无符号右移6位后的十进制:"+(b>>>6));
System.out.println("操作后二进制:"+Integer.toBinaryString(b>>>6));
}

在这里我没有使用=进行重新赋值,而是计算完成后直接打印十进制和二进制的结果。

操作前十进制:-1

操作前二进制:11111111111111111111111111111111

进行无符号右移6位后的十进制:67108863

操作后二进制:11111111111111111111111111

从打印结果中可以明显的看出是先转换为int类型,然后进行位运算,位运算结束后由于重新赋值所以进行的截断。

对于long类型,它是64位,不用先转换。

通过上面的实例解析加上动力节点在线的免费视频课程,我们不难得出结论,移位符是Java中的基本操作符,实际支持的类型只有int和long。在对byte,short,char类型进行移位操作时,都会先将其转换为int类型再进行操作。左移<<操作符相当于乘以2。带符号右移操作符>>相当于除以2。在Java中使用位运算符会比乘*,除/运算符更高效一些。而无符号右移符>>>在移动时高位补零,低位丢弃,在正数时仍然相当于除以2,但是在负数时结果却是变大了(由负数变为正数)。

0人推荐
共同学习,写下你的评论
0条评论
代码小兵652
程序员代码小兵652

113篇文章贡献392215字

相关课程 更多>

作者相关文章更多>

推荐相关文章更多>

Java面试题及答案整理

提枪策马乘胜追击04-21 20:01

Spring常见面试题

代码小兵92504-17 16:07

Java零基础实战项目——五子棋

代码小兵98804-25 13:57

Java string类详解

杨晶珍05-11 14:54

6道经典算法面试题

杨晶珍05-12 16:39

发评论

举报

0/150

取消