# Java 中的移位运算符（Shift Operator）

**URL:** <https://www.isharkfly.com/t/java-shift-operator/14594>\
**Category:** 程序语言\
**Tags:** java\
**Created:** [September 3, 2023, 4:06pm UTC](https://www.isharkfly.com/t/java-shift-operator/14594 "2023-09-03T16:06:32Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![honeymoose](https://www.isharkfly.com/user_avatar/www.isharkfly.com/honeymoose/32/20796_2.png) [@honeymoose](https://www.isharkfly.com/u/honeymoose)\
**Post date:** [September 3, 2023, 4:06pm UTC](https://www.isharkfly.com/t/java-shift-operator/14594/1 "2023-09-03T16:06:33Z")

</div>

针对移位（Shift Operator）操作符是最基本的操作符之一，几乎每种编程语言都包含这一操作符。

同时我们对移位运算又会觉得比较陌生和困惑，这是因为移位运算除了在 JDK 底层你会遇到不少，还有就是在各种奇葩的面试题会遇到一些，在实际使用的时候，这个运算其实很难用得上。

因为用得不多，所以在大部分人的面对的代码情况下，根本不会考虑移位运算，所以对移位运算我们大致知道下就可以了，至于如何奇葩的运算，你只知道一些基本概念就行，其实很多时候并不需要你直接用移位运算算出来。

## 基本概念

针对移位运算，我们需要了解有几个基本概念。

### 3 个移位运算符

Java 只有 3 个移位运算符， `<<` （左移）、 `>>` （带符号右移）和 `>>>` （无符号右移）。

为什么有 3 个，移位运算不是左就是右，为什么有 3 个？

因为 Java 的整数是有符号的整数，所以针对符号转换 Java 添加了一个无符号右移。

### 只能用于整数

Java 的移位运算，不能用于浮点数，只能用于整数。

因为 Java 可以处理整数的长度不一样，所以移位运算只会用在 int 上，虽然其他数据类型也可以用，但是都是在转换成 int 后进行计算的。

| 类型 | 长度 |
| --- | --- |
| long | 64 位 |
| int | 32 位 |
| short | 16 位 |
| byte | 8 位 |
| char | 16 位 |

### 整数 2 进制表达

在 Java 的整数 int 表达中，其中有一个位留给了符号位置，所以真正可以存储数据的位为 31 位。

因此，Int 的存储范围为：[-2^31,2^31-1]，所以上面的指数为 31， 而不是 32 的原因是其中有一位留给了符号位。

## 左移操作符 \<\<

左移操作符 `<<` 是将数据转换成二进制数后， **向左移若干位，高位丢弃，低位补零** 。

如下面的代码：

```auto
        log.debug("{}/{}", Integer.toBinaryString(12), Integer.parseInt(Integer.toBinaryString(12), 2));
        log.debug("{}/{}", Integer.toBinaryString(12 << 1), Integer.parseInt(Integer.toBinaryString(12 << 1), 2));
        log.debug("{}/{}", Integer.toBinaryString(12 << 8), Integer.parseInt(Integer.toBinaryString(12 << 1), 2));

```

程序的输出为：

```auto
12:16:18.985 [main] DEBUG com.ossez.toolkits.codebank.tests.EmptyQuickTest - 1100/12
12:16:18.986 [main] DEBUG com.ossez.toolkits.codebank.tests.EmptyQuickTest - 11000/24
12:16:18.986 [main] DEBUG com.ossez.toolkits.codebank.tests.EmptyQuickTest - 110000000000/24

```

因为都在末尾补 0 ，所以在范围内，不管你是左移 1 位还是超过 1 位，都是等于 10 进制的数乘以 2。

![2023-09-03_12-16-42](https://www.isharkfly.com/images/transparent.png)

## 带符号右移操作符 \>\>

Java中整型表示负数时，最高位为符号位，正数为`0` ，负数为`1` 。

`>>` 是带符号的右移操作符，将数据转换成二进制数后， **向右移若干位，高位补符号位，低位丢弃** 。

对于正数作右移操作时，具体体现为高位补`0` ；负数则补`1`

这个主要是针对右移动的时候高位出现空白，我们应该还是补 0 还是 1 的问题。

带符号的右移意思就是：当高位出现空白的时候，我们补符号位，根据当前的数据不同而不同。

如下面的代码：

```auto
        log.debug("{}", Integer.toBinaryString(-12));
        log.debug("{}", Integer.toBinaryString(-12 >> 1));
        log.debug("{}", Integer.toBinaryString(-12 >> 8));

```

运行结果为：

```auto
12:25:32.765 [main] DEBUG com.ossez.toolkits.codebank.tests.EmptyQuickTest - 11111111111111111111111111110100
12:25:32.765 [main] DEBUG com.ossez.toolkits.codebank.tests.EmptyQuickTest - 11111111111111111111111111111010
12:25:32.765 [main] DEBUG com.ossez.toolkits.codebank.tests.EmptyQuickTest - 11111111111111111111111111111111

```

我们可以看到上面的移位为带有符号的移位置，所有移动的高位在负数的时候都被补充为符号位了。

如果是负数的话，就会补充为 1 。

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

无符号右移操作符 `>>>` 与`>>` 类似，都是将数据转换为二进制数后右移若干位，不同之处在于，不论负数与否，结果都 是 **高位补零，低位丢弃** 。

这个操作符的计算对负数的计算会因为补位的不同而变成整数。

如下面的代码。

```auto
        log.debug("---- Shift Operator >>> ---");
        log.debug("{}", Integer.toBinaryString(-12));
        log.debug("{}/{}", Integer.toBinaryString(-12 >>> 1), Integer.parseInt(Integer.toBinaryString(-12 >>> 1), 2));
        log.debug("{}/{}", Integer.toBinaryString(-12 >>> 8), Integer.parseInt(Integer.toBinaryString(-12 >>> 8), 2));

```

程序输出如下：

```auto
13:25:19.374 [main] DEBUG com.ossez.toolkits.codebank.tests.EmptyQuickTest - ---- Shift Operator >>> ---
13:25:19.374 [main] DEBUG com.ossez.toolkits.codebank.tests.EmptyQuickTest - 11111111111111111111111111110100
13:25:19.374 [main] DEBUG com.ossez.toolkits.codebank.tests.EmptyQuickTest - 1111111111111111111111111111010/2147483642
13:25:19.374 [main] DEBUG com.ossez.toolkits.codebank.tests.EmptyQuickTest - 111111111111111111111111/16777215

```

![2023-09-03_13-27-11](https://www.isharkfly.com/images/transparent.png)

从上面的代码输出中，我们会发现对应的 2 进制长度不一样，因为在 Java 程序中对于二进制，前面为 0 的时候，在输出的时候会进行丢弃的。

所以显示的长度不一样，如果希望显示长度一致的话，前面补 0 就可以了。
