Java位运算符详解

# 前言

之前了解过位运算符,左移`>`等于除以2。但是我在看jdk源码的时候发现了一个`>>>`三个符号的,不明白这是什么意思,就去搜了一下,发现还挺多的知识点的,就整理了一下。

首先我们知道,我们编写的程序最终都是在计算机底层进行的,计算机底层也仅支持0、1两种符号。所以当时网上有个键盘只有0、1两个键,那才是大佬用的键盘。扯远了。。。

先来复习一下java的基本类型都占多少字节,占多少位(1字节等于8位):

| 类型 | 字节数 | 位数 | 大小范围 |
| ------- | ------ | ---- | ---------------------------- |
| byte | 1 | 8 | -2^8^~2^8^-1 |
| short | 2 | 16 | -2^16^~2^16^-1 |
| int | 4 | 32 | -2^32^~2^32^-1 |
| long | 8 | 64 | -2^64^~2^64^-1 |
| float | 4 | | |
| double | 8 | | |
| char | 2 | 16 | 一个char类型可以存储一个汉字 |
| boolean | 1 | | true or false |

移位操作是把数据看作二进制数,然后将其向左或向右移动若干位的运算。在Java中,移位操作符包含三种:`>`带符号右移运算符,`>>>`无符号右移运算符。这三种操作符都只能作用于`long`,`int`,`short`,`byte`这四种基本整形类型上和`char`类型上。其他类型如double都无法使用位运算符,大家可以在ide中自行试验一下。

在java中,第一位用来表示数字的正负,第一位为零时表示正数,第一位为1时表示负数。我们拿最简单的8位byte类型举例:`0000 0000`表示0,`0111 1111`这个表示最大值(2^8^-1),再进行加一后就变成了`1000 0000`这时就变成了最小值(-2^8^)。再加一后变成`1000 0001`这时的值为-127。也就是从0到最大值然后转为最小值,然后再从最小值向零靠近。

# 左移操作符`>`

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

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

再来用例子证明一下:

```java
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位。分别将它们的移动前后二进制和十进制打印出来查看。

```java
a右移前的二进制:10000000000
a右移后的二进制:1000000
a右移后的十进制:64
b右移前的二进制:11111111111111101110110101000000
b右移后的二进制:11111111111111111110111011010100
b右移后的十进制:-4396
```

a原来的二进制向右移动后,低位被丢弃,高位补充符号位也就是0。b原来的二进制向右移动后,低位被丢弃,高位补充符号位1。这也符号我们之前的运算规律:
1024 / 2^4^ =16 ;-70336/ 2^4^ = -4396。

# 无符号右移操作符`>>>`

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

再用例子来证明一下:

```java
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);
}
```

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

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

```java
a右移前的二进制:10000000000
a右移后的二进制:1000000
a右移后的十进制:64
b右移前的二进制:11111111111111101110110101000000
b右移后的二进制:1111111111111110111011010100
b右移后的十进制:268431060
```

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

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

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

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

```java
public static void main(String[] args) {
int a = 10;
a >>= 6;
System.out.println("操作后:"+b);
}
```

定义了byte的值为-1,即`1111 1111`,然后无符号右移6位,高位补零,低位丢弃,那么应该变成`0000 0011`也就是是3。让我们运行一下这段代码看一下打印出来的信息是不是3呢?

```java
操作前:-1
操作后:-1
```

运行结果与我们预期的结果不一致!

我们将它的二进制也一起打印出来看一下究竟:

```java
public static void main(String[] args) {
byte b = -1;
System.out.println("操作前十进制:"+b);
System.out.println("操作前二进制:"+Integer.toBinaryString(b));
b >>>= 6;
System.out.println("操作后二进制:"+Integer.toBinaryString(b));
System.out.println("操作后十进制:"+b);
}
```

这时再看一下运行结果

```java
操作前十进制:-1
操作前二进制:11111111111111111111111111111111
操作后二进制:11111111111111111111111111111111
操作后十进制:-1
```

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

```java
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));
}
```

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

```java
操作前十进制:-1
操作前二进制:11111111111111111111111111111111
进行无符号右移6位后的十进制:67108863
操作后二进制:11111111111111111111111111
```

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

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

# 总结

移位符是Java中的基本操作符,实际支持的类型只有`int`和`long`。在对`byte`,`short`,`char`类型进行移位操作时,都会先将其转换为`int`类型再进行操作。左移`>`相当于除以2。在Java中使用位运算符会比乘`*`,除`/`运算符更高效一些。而无符号右移符`>>>`在移动时高位补零,低位丢弃,在正数时仍然相当于除以2,但是在负数时结果却是变大了(由负数变为正数)。

> 本文由博客一文多发平台 [OpenWrite](https://openwrite.cn?from=article_bottom) 发布!
博主邮箱:[email protected],有问题可以邮箱交流。

原文地址:https://www.cnblogs.com/lnj96/p/12134387.html

时间: 2024-10-07 23:27:37

Java位运算符详解的相关文章

Java的位运算符详解实例

Java的位运算符详解实例——与(&).非(~).或(|).异或(^).右移(>>).左移(<<).无符号右移(>>>) 位运算符主要针对二进制, 它包括了:“与”.“非”.“或”.“异或”."右移"."左移"."无符号右移". 从表面上看似乎有点像逻辑运算符, 但逻辑运算符是针对两个关系运算符来进行逻辑运算, 而位运算符主要针对两个二进制数的位进行逻辑运算. 下面详细介绍每个位运算符. 1.与运

[转]Java的位运算符详解实例——与(&amp;)、非(~)、或(|)、异或(^)

位运算符主要针对二进制,它包括了:“与”.“非”.“或”.“异或”.从表面上看似乎有点像逻辑运算符,但逻辑运算符是针对两个关系运算符来进行逻辑运算,而位运算符主要针对两个二进制数的位进行逻辑运算.下面详细介绍每个位运算符. 1.与运算符与运算符用符号“&”表示,其使用规律如下:两个操作数中位都为1,结果才为1,否则结果为0,例如下面的程序段.public class data13{public static void main(String[] args){int a=129;int b=128

java移位运算符详解[转]

java移位运算符不外乎就这三种:<<(左移).>>(带符号右移)和>>>(无符号右移). 1. 左移运算符 左移运算符<<使指定值的所有位都左移规定的次数. 1)它的通用格式如下所示: value << num num 指定要移位值value 移动的位数. 左移的规则只记住一点:丢弃最高位,0补最低位 如果移动的位数超过了该类型的最大位数,那么编译器会对移动的位数取模.如对int型移动33位,实际上只移动了332=1位. 2)运算规则 按

java移位运算符详解

java移位运算符不外乎就这三种:<<(左移).>>(带符号右移)和>>>(无符号右移).1.左移运算符左移运算符<<使指定值的所有位都左移规定的次数.1)它的通用格式如下所示:value << numnum 指定要移位值value 移动的位数.左移的规则只记住一点:丢弃最高位,0补最低位如果移动的位数超过了该类型的最大位数,那么编译器会对移动的位数取模.如对int型移动33位,实际上只移动了332=1位. 2)运算规则按二进制形式把所有的

Java千问:Java语言位运算符详解

很多编程语言都有位运算符,Java语言也不例外.在Java语言中,提供了7种位运算符,分别是按位与(&).按位或(|).按位异或(^).取反(~).左移(<<).带符号右移(>>)和无符号右移(>>>).这些运算符当中,仅有~是单目运算符,其他运算符均为双目运算符.在讲解这些运算符的使用之前,必须了解一个常识,那就是:位运算符是对long.int.short.byte和char这5种类型的数据进行运算的,我们不能对double.float和boolean进

Java的位运算符详解实例——与(&amp;)、非(~)、或(|)、异或(^)

位运算符主要针对二进制,它包括了:"与"."非"."或"."异或".从表面上看似乎有点像逻辑运算符,但逻辑运算符是针对两个关系运算符来进行逻辑运算,而位运算符主要针对两个二进制数的位进行逻辑运算.下面详细介绍每个位运算符. 1.与运算符 与运算符用符号"&"表示,其使用规律如下: 两个操作数中位都为1,结果才为1,否则结果为0,例如下面的程序段. public class data13 { publ

6.Java逻辑运算符和位运算符详解

一.逻辑运算符 逻辑运算符共有三种,即“非”.“和”.“或”,分别用 " ! ".“&&”.“||”表示.其中: 非运算(!)表示否定,如:!true等于false.!false等于true.!2等于false.!0等于true. 和运算(&&)前后两个条件都为真时,才返回true,否则返回false. 或运算(||)前后两个条件有一个为真是,返回true,都为假时,返回false. public class data10{     public sta

java中运算符详解

前言 运算符用于执行程序代码运算,会针对一个以上操作数项目来进行运算.JAVA中常见的运算符有很多种,大致分为以下几种,常见的几种运算符如下图: 算术运算符 加.减.乘.除.求余.例++.--.%./.  赋值运算符 为变量或常量起到赋值作用的.例如=.+=.*= 关系运算符 判断数据大小的,结果为一个布尔值.例如>.>=.!=.== 逻辑运算符 进行逻辑运算,运算的成员为布尔值,结果也为布尔值.例如&&.||.!. 条件运算符 也称三目运算符,表达式为(a<b)?a:b

位运算符详解

运算符   含义   &   按位与   |   按位或   ∧   按位异或   ∽   取反   <<   左移   >>   右移 说明: 1.位运算符中除∽以外,均为二目(元)运算符,即要求两侧各有一个运算了量. 2.运算量只能是整形或字符型的数据,不能为实型数据. “按位与”运算符(&) 规定如下: 0&0=0   0&1=0   1&0=0   1&1=1 例:3&5=?