---
url: /doc/Java/gj3agmnn/index.md
---
# 运算符

> 运算符：一种特殊的符号，用于表示数据的运算、赋值和比较等。

## 算数运算符

> 算术运算符是对数值类型的变量进行运算的运算符，在 Java 程序中使用得非常多。其运算结果是一个数值量。

* `+` ; `-` ; `*` ; `/` ：加（正）、减（负）、乘、除

  > `5 / 2 = 2;` 因为是 `int`。同理 `5.0 / 2 = 2.5`

* `%` ：取模（求余数），结果和被取模数同号。其实 `a % b == a - (int)a / b * b;`

  > `11 % 9 = 2;`
  >
  > `-11 % 9 = -2;`
  >
  > `11 % -9 = 2;`
  >
  > `-11 % -9 = -2;`

* `++` ：自增。

  `++i` 先自增后赋值；`i++` 先赋值后自增

  ```
  int i = 10;
  int j = ++i; 	//等价于 i = i + 1; j = i; 此时 i = 10; j = 10`
  int k = i++; 	//等价于 k = i; i = i + 1; 此时 i = 11; k = 10`
  i = i++; 		//系统会先后执行 int temp = i;     i = i + 1;     i = temp
  i = ++i; 		//系统会先后执行 i = i + 1;     int temp = i;     i = temp
  ```

* `--` ：自减。和 `++` 同理。

* `+` ：字符串相加

## 关系运算符（比较运算符）

> 关系运算符结果都为 `boolean` 型，要么是 `ture` 要么是 `false`。其关系表达式经常用在 `if` 结构的条件中或循环结构的条件中。

* `==` ：相等于。`8 == 7` 结果 `false`
* `!=` ：不等于
* `<` `>` ：小于、大于
* `<=` `>=` ：小于等于、大于等于
* `instanceof` ：检查是否是类的对象。`"a" istanceof String` 结果 `ture`

### 使用细节

1. 关系运算符结果都是 `boolean` 型，要么是 `ture` ，要么是 `false`
2. 关系运算符的表达式，称为*关系表达式*
3. 比较运算符 `==` 不要误写为 `=`
4. Java 允许将 `==`、`!=` 两种运算用于任何数据类型

## 逻辑运算符

> 用于连接多个条件（多个关系表达式），最终的结果也是一个 `boolean` 值。

* `&&` ：短路与。`a` `b` 同时为 `ture`，则结果为 `ture`，否则为 `false`。

* `&` ：逻辑与。`a` `b` 同时为 `ture`，则结果为 `ture`，否则为 `false`。

  `&&` 与 `&` 的区别，在于 `a && b` 的场合，`a = false` 时，则 `b` 不判断。而 `&` 会完成判断。开发中多用 `&&` ，因为其效率更高。

  ```
  int a = 1;
  int b = 1;

  if (a++ > 1 && ++b < 1) System.out.println("Nothing happened");
  /*
  此时 a 经历了先判断后自增，返回 false 并且 a = 2
  但此时 b = 1; 因为 a = false; 所以 ++b 不执行。
  	*/
  ```

* `||` ：短路或。`a` `b` 任一为 `ture`，则结果为 `ture`，否则为 `false`。

* `|` ：逻辑或。`a` `b` 任一为 `ture`，则结果为 `ture`，否则为 `false`。

  > 两者的区别和`&&` 与 `&` 相似，若第一个为 `ture` ，则 `||` 不会判断第二个。

* `!` ：取反。`a` 为 `ture`，则结果为 `false`。反之为 `ture`。

* `^`：逻辑异或。`a` `b` 不同时，结果为 `ture`，否则为 `false`。

## 赋值运算符

> 将某个运算后的值，赋给指定变量

* 基本赋值运算符：`=`

* 符合赋值运算符：`+=` ; `-=` ; `*=` ; `/=` ; `%=` 等

  > `a += b` 等价于 `a = a + b`。其余同理。

### 使用细节

1. 运算顺序从右往左。

   ```
   int num = a + b + c;	// 先运行(a + b + c)，再结算int num =

   ```

2. 运算符左边只能是变量，右边可以是变量、表达式、常量值。

3. 复合赋值运算符会进行类型转换。

   ```
   byte b = 2; b += 3;		// 此时 b += 3 等价于 b = (byte)(b + 3)
   b++;					// 同理
   ```

## 三元运算符

> 基本语法：`条件表达式 ? 表达式1 : 表达式2;`
>
> 运算规则：如果条件表达式为 `ture` ，运算的结果是表达式 1；反之为表达式 2。
>
> ```
> int a = 10;
> int b = 11;
> int result = (a == b ? a++ : b++);		// 此时 a = 10  result = 11  b = 12
> ```

### 使用细节

1. 表达式 1 和 表达式 2 要为可以赋给接受变量的类型（或可以自动转换，或进行强制转换）

2. 三元运算符可以转成 `if--else`

3. 三元运算符是一个整体。

   ```
   Object obj = true ? new Integer(1) : new Double(2.2);
   System.out.print(obj)
   ```

   上例中，系统将会输出 `1.0`

   因为 三元运算符 是一个整体，所以根据 \[2.8.1.1.1] 发生了类型转换。

## 运算符优先级

[详细优先级](http://c.biancheng.net/view/794.html)

| 运算符（优先级从高到低）                                                  | 结核性                   |
| ------------------------------------------------------------------------- | ------------------------ |
| `[]`、`()` 方法调用                                                       | 从左向右                 |
| `!`、`~`、`++`、`--`、`+`（一元运算）、`-`（一元运算）、强制类型转换、new | 从右向左                 |
| `*`、`/`、`%`                                                             | 从左向右                 |
| `+`、`-`                                                                  | 从左向右                 |
| `<<`、`>>`、`>>>`                                                         | 从左向右                 |
| `<`、`>`、`<=`、`>=`、instanceof                                          | 从左向右                 |
| `==`、`!=`                                                                | 从左向右                 |
| `&`                                                                       | 从左向右                 |
| `^`                                                                       | 从左向右                 |
| `                                                                        |`                        |
| `&&`                                                                      | 从左向右                 |
| `                                                                        |                          |
|`?:`（三元运算符）                                                        | 从右向左                 |
| `=`、`+=`、`-=`、`\*=`、`/=`、`&=`、`%=`、`^=`、`                          | =`、`<<=`、`>>=`、`>>>=\` |

1. 运算符有不同优先级。优先级高的总是优先于低的。
2. 只有单目运算符、赋值运算符是从右向左运算的。

## 标识符

> Java 对各种变量、方法和类等命名时使用的字符序列称为标识符
>
> 凡是自己可以起名字的地方都叫标识符 `double height = 0.0;`

### 命名规则

1. 标识符由 26 个大、小写英文字母，0 - 9 阿拉伯数字，\_ 或 $ 符号组成。

2. **数字不能开头**。 错误示范：`int 3a = 1;`

3. 不能使用关键字和保留字（可以包含）。\[具体的关键字和保留字请自行查询]\(https://i-melody.github.io/2021/11/23/Java/入门阶段/3 运算符/www.baidu.com)

4. 严格区分大小写，长度无限制。

5. 不能包含空格。

6. 与多数编程语言不同。 可以用任何 Unicode 字符（特殊字符除外）作为标识符。但不推荐这么做

   > **来 String 一只猫：**

   ```
   String ᓚᘏᗢ = "✪ ω ✪";				// 喵？

   ```

### 命名规范

1. 包名：多单词组成时所有字母都小写：aaa.bbb.ccc
2. 类名、接口名：多单词组成时，采用大驼峰法，所有单词的首字母大写：XxxYyyZzz
3. 变量名、方法名：多单词组成时，采小驼峰法（驼峰法），第一个单词首字母小写，第二个开始每个单词首字母大写：xxxYyyZzz
4. 常量名：所有字母都大写。多单词时每个单词用下划线连接：XXX\_YYY\_ZZZ
5. 更详细的规则查看 [Java 编码规范](https://developer.aliyun.com/article/709470)

## 输入与输出

> 为满足读取用户输入、输出的需求，Java 提供了几个基本类

### Scanner 类

> 在编程中，需要接收用户输入的数据，就可以使用键盘输入语句来获取。
>
> `Input.java` ，需要一个 扫描器（对象），就是 Scanner
>
> Scanner 属于 java.util 包。其包含许多方法

**示例：**

```
import java.util.Scanner;		//[1] 导入

    public class Code3_8_1{
    public static void main(String[] args){
        Scanner aScannerName = new Scanner(System.in);	//[2] 创建 Scanner 对象
        System.out.println("\n输入点什么吧！");
        String pointSomething = aScannerName.next();	//[3] 接收用户输入
        System.out.println("\n接下来，输入一个数字吧！");
        double aNumber = aScannerName.nextDouble();		//[3] 接收用户输入
        System.out.println("\n你输入的\"点什么\"是:\""
        	+ pointSomething + "\"\n你输入的\"一个数字\"是：\""
        	+ aNumber + "\"");
    }
}
```

> 1. 导入该类所在的包
> 2. 创建该类对象（声明变量）
> 3. 调用里面的功能，接收用户输入

构造方法：

* `new Scanner(System.in)`：该方法接收一个参数（输入源）。其中 `System.in` 代表标准输入流。默认情况指键盘。

  ***—— 见 \[\[17.2.3.4 标准输入 / 输出流]]\(https://i-melody.github.io/2022/01/06/Java/入门阶段/17 IO 流/#17-2-3-4-标准输入-x2F-输出流)***

常用方法：

* `String next()`：读取下一个输入对象

  Scanner 对象用空白（空格、水平制表符或回车换行符）作为输入的分隔元素

* `Double nextDouble()`：读取下一个 double

  `Int nextInt()`：读取下一个 int

* `String nextLine()`：读入当前行的所有输入，直到行尾

* `boolean hasNext()`：输入中是否还有其他单词

  `boolean hasNextInt()`、`boolean hasNextDouble()`

### 格式化输出

> System.out 标准输出流。调用该流方法以输出内容至控制台窗口

* `println(String s)`：输出字符，结束后换行

  `print(String s)`：输出字符，结束后不换行

  `println(int n)`、`println(char c)`、`println(Object obj)`……

* `printf(String format, Object ... args)`：格式化输出

  ```
  System.out.printf("%s，你来啦，给你 %d 拳", "柏枝凪斗", 1);	// <———— 给了柏枝凪斗一拳

  ```

  以 % 开头的 *格式说明符* 都用相应参数替换。格式说明符尾部的转换符表示要格式化的数值类型

  | 转换符 | 类型           |     | 转换符   | 类型                   |
  | ------ | -------------- | :-: | -------- | ---------------------- |
  | d      | 十进制整数     |     | s        | 字符串                 |
  | x      | 十六进制整数   |     | c        | 字符                   |
  | o      | 八进制整数     |     | b        | 布尔值                 |
  | f      | 定点浮点数     |     | h        | 散列码                 |
  | e      | 指数浮点数     |     | tx 或 Tx | 日期时间（T 强制大写） |
  | g      | 通用浮点数     |     | %        | 百分号                 |
  | a      | 十六进制浮点数 |     | n        | 行分隔符               |

  也能指定控制格式化输出外观的各种标志

  ```
  System.out.printf("% (4.12f", Math.PI);	// <————— 保留 12 位，正数添加空格，负数添加括号

  ```

  | 标志                  | 目的                                                           |
  | --------------------- | -------------------------------------------------------------- |
  | +                     | 打印正数和负数的符号                                           |
  | （空格）              | 在正数前添加空格                                               |
  | 0                     | 数字前面补 0                                                   |
  | -                     | 左对齐                                                         |
  | (                     | 负号被括号环绕                                                 |
  | ,                     | 添加分组分隔符                                                 |
  | #（对于 f 格式）      | 包含小数点                                                     |
  | #（对于 x 或 0 格式） | 添加前缀 0x 或 0                                               |
  | $                     | 指定要格式化的参数索引：%1$d 以十进制打印第 1 个参数           |
  | <                     | 格式化前面说明的数值：%d%\<x 十进制打印后，再以十六进制打印一遍 |

### NumberFormat 类

> Java 提供的格式化输出功能，能使打印或显示是信息更美观
>
> NumberFormat 类属于 Java 标准类库，在 java.text 包中
>
> NumberFormat 类不能直接创建对象。利用类中的静态方法获取一个对象实例

获取实例：

* `NumberFormat.getInstance()`：返回当前默认语言环境的默认数值格式
* `NumberFormat.getCurrnecyInstance()`：返回当前默认语言环境的通用格式
* `NumberFormat.getNumberInstance()`：返回当前默认语言环境的通用数值格式
* `NumberFormat.getPercentInstance()`：返回当前默认语言环境的百分比格式

常用方法：

* `format(num)`：获取 num 的格式化语句

* `setMaximunFactionDigits(n)`：将该格式的小数部分允许的最大位数设置为 n

  `setMinimunFactionDigits(n)`：将该格式的小数部分允许的最小位数设置为 n

* `setMaximunIntegerDigits(n)`：将该格式的整数部分允许的最大位数设置为 n

  `setMinimunIntegerDigits(n)`：将该格式的整数部分允许的最小位数设置为 n

### DecimalFormat 类

> 与 NumberFormat 类不同，DecimalFormat 类可以直接实例化对象。

实例化：

* `new DecimalFormat(pattern)`

  其中 pattern 是一个 String，代表格式化处理模式。如

  ```
  DecimalFormat df = new DecimalFormat("0.###");		//格式对象，保留 3 位小数

  ```

常用方法：

* `format(num)`：获取 num 的格式化语句
* `applyPattern(pattern)`：变更要使用的格式

## 进制

* 二进制：数字有 0 - 1，满 2 进 1，以 `0b` 或 `0B` 开头
* 十进制
* 八进制：0 - 7，满 8 进 1，以 `0` 开头表示
* 十六进制：0 - 9 及 A - F，满 16 进 1，以`0x` 或 `0X` 开头。此处的 A - F 不分大小写。

### 进制的转换

* 其他进制 转 十进制：略
* 十进制 转 其他进制：将该数不断除以对象进制数，直到商为 0 为止，将每步得到的余数倒过来。
* 二进制 与 八进制 或 十六进制 互相转换：二进制 从低位开始，每 3 位一组，转成对应的 八进制 即可。反之同理。十六进制亦同理（每 4 位一组）。

### 原码、反码、补码

对于有符号数而言：

1. 二进制的最高位是符号位：0 表示正数，1 表示负数。
2. 正数的原码、反码、补码都一样。
3. 负数的反码 = 原码符号位不变，其他位取反（0 变 1，1 变 0）
4. **负数的补码 = 反码 + 1。也就是说，负数的反码 = 补码 - 1。**
5. 0 的反码、补码都是 0。
6. Java 没有无符号数。换言之，Java 的数都是有符号的。
7. **计算机运算时，都是以补码的方式来运算的。**
8. **当我们看运算结果时，要看其原码。**

## 位运算符

* `&` 按位与：两位都为 1，结果为 1，否则为 0

* `|` 按位或：两位有一个为 1，结果为 1，否则为 0

* `^` 按位异或：两位不同，结果为 1，否则为 0

* `~` 按位取反：把 0 变 1，1 变 0

  以下是几个示例：

  * `2 & 3`：

```
2 的原码：00000000 00000000 00000000 00000010
2 的补码：00000000 00000000 00000000 00000010
3 的原码：00000000 00000000 00000000 00000011
3 的补码：00000000 00000000 00000000 00000011
补码运算结果：00000000 00000000 00000000 00000010
结果转成原码：00000000 00000000 00000000 00000010
运算结果：2
```

* `~-2`：

```
得到 -2 的原码：10000000 00000000 00000000 00000010
得到 -2 的反码：11111111 11111111 11111111 11111101
得到 -2 的补码：11111111 11111111 11111111 11111110
补码运算结果：00000000 00000000 00000000 00000001
结果转成原码：00000000 00000000 00000000 00000001
运算结果：1
```

* `~2`：

```
2 的原码：00000000 00000000 00000000 00000010

2 的补码：00000000 00000000 00000000 00000010

补码运算：11111111 11111111 11111111 11111101

结果转成反码：11111111 11111111 11111111 11111100

反码转成原码：10000000 00000000 00000000 00000011

运算结果：-3
```

* `>>` 算术右移：低位溢出，符号位不变，用符号位补溢出的高位

* `<<` 算数左移：符号位不变，低位补 0

* `>>>` 逻辑右移（无符号右移）：低位溢出，高位补 0

  下面是示例：

  * 将数字 1 算术右移 2 位：`int a = 1 >> 2`

```
1 的补码：00000000 00000000 00000000 00000001

补码结果：00000000 00000000 00000000 00000000

结果：00000000 00000000 00000000 00000000
```

* 将数字 1 算术左移 2 位：`int b = 1 << 2`

```java
1 的补码：00000000 00000000 00000000 00000001

补码结果：00000000 00000000 00000000 00000100

结果：00000000 00000000 00000000 00000100
```

也就是说，`1 << 2` 本质是 `1 * 2 * 2 = 4`

而 `1 >> 2` 本质是 `1 / 2 / 2 = 0`

**使用细节：**

1. 位运算符只对整型、字符类型有效

2. 移位运算中，左侧操作 int 的场合，右侧操作数以 32 取模；long 的场合，右侧操作数以 64 取模。

   ```
   int n = 20;
   n >>= 32;			//这个场合，n 不改变。这样能保证左侧数字不被全部移走
   ```
