# Java教程 - 3 运算符
在编写有逻辑的代码前,先学习一下 Java 中的运算符。
下面介绍 Java 中常见的运算符:算数运算符、赋值运算符、关系运算符。
# 3.1 算数运算符
算数运算符是干嘛的?
就是进行数学运算的,就是小学学的加、减、乘、除等。
Java 中常见的算数运算符如下:
| 运算符 | 描述 | 示例 | 
|---|---|---|
| + | 加法 | a + b | 
| - | 减法 | a - b | 
| * | 乘法 | a * b | 
| / | 除法 | a / b | 
| % | 取余,模运算 | a % b | 
举个例子:
public class HelloJava {
    public static void main(String[] args) {
        int a = 10;
        int b = 3;
        System.out.println(a + b); 		// 输出 13
        System.out.println(a - b); 		// 输出 7
        System.out.println(a * b); 		// 输出 30
        System.out.println(a / b); 		// 输出 3
        System.out.println(a % b); 		// 输出 1
    }
}
2
3
4
5
6
7
8
9
10
11
12
注意:两个整数相除,结果还是整数,小数部分会被去掉。
如果想让两个整数相除,保留小数部分,可以先乘上1.0,举个栗子:
int a = 10;
int b = 3;
System.out.println(1.0 * a / b); 		// 输出 3.3333333333333335
2
3
4
加减乘除在运算中用的是最多的,但是模运算有时候也会用到,下面举个栗子:
求1789的十位数是多少?
int i = 1789;
int j = i / 10 % 10;
System.out.println(j);  // 结果为:8
2
3
因为 i 是整形的,所以 i / 10 只会保留整数部分,得到 178,178 % 10,余数是 8。
同样,如果判断一个整数是否是偶数,那么可以将这个数 % 2,然后查看结果是否是 0,如果是 0 就是偶数,否则是奇数。
算数运算符也是有优先级的,和在小学的时候学习的一样。
int i = 3 + 2 * 4;
System.out.println(i);  // 结果为:11
2
乘、除、取模运算的优先级大于加、减运算。
# 3.2 赋值运算符
赋值运算符就是用来给变量赋值的。
# 赋值运算符
| 运算符 | 描述 | 举例 | 
|---|---|---|
| = | 赋值运算符 | 就是把 = 右边的值赋值给左边的变量,之前用过了。 | 
例如:
int a = 2;
int b = 3;
int c = a + b;
2
3
除了赋值运算符,下面还有复合赋值运算符。
# 复合赋值运算符
复合赋值运算符就是经过计算后,将值赋给前面的变量。
Java 中常用的复合赋值运算符如下:
| 运算符 | 描述 | 举例 | 
|---|---|---|
| += | 加法赋值运算符 | c += a 等效于 c = c + a | 
| -= | 减法赋值运算符 | c -= a 等效于 c = c - a | 
| *= | 乘法赋值运算符 | c *= a 等效于 c = c * a | 
| /= | 除法赋值运算符 | c /= a 等效于 c = c / a | 
| %= | 取模赋值运算符 | c %= a 等效于 c = c % a | 
| ~/= | 整除赋值运算符 | c ~/= a 等效于 c = c ~/ a | 
举个栗子:
public static void main(String[] args) {
    int a = 5;
    int b = 3;
    a += b; // a = a + b
    System.out.println(a); // 8
    a = 5;
    a -= b; // a = a - b
    System.out.println(a); // 2
    a = 5;
    a *= b; // a = a * b
    System.out.println(a); // 15
}
2
3
4
5
6
7
8
9
10
11
12
13
14
复合赋值运算符感觉这么写可读性还变差了,有什么好处呢?
- 更加简练;
- 避免类型转换的麻烦;
是如何避免类型转换的,举个栗子:
short s = 10;
// s = s + 2;							// 编译失败,因为得到的结果是int类型,无法赋值给short
s += 2;	    							// 可以,复合赋值运算符不会改变变量本身的数据类型
System.out.println(s);    // 12
2
3
4
复合赋值运算符运算完成,类型是不变的,如果使用普通方式,则需要强制转换 s = (short)(s + 2)。
# 3.3 自增自减运算符
Java中的自增自减运算符包括++和--。它们可以用于对变量进行加1或减1操作。
举个栗子:
public static void main(String[] args) {
    int a = 5;
    a++; // a自增1
    System.out.println(a); // 输出 6
    a--; // a自减1
    System.out.println(a); // 输出 5
}
2
3
4
5
6
7
需要注意的是,自增自减运算符可以放在变量的前面或后面,这会影响到表达式的值。
如果运算符放在变量的前面,表示先加减,后使用;如果运算符放在变量的后面,表示先使用,后加减。
举个栗子:
public static void main(String[] args) {
    int a = 5;
    int b = a++; 		// 先使用a的值还是5,后加1,a的值变为6
    System.out.println(b); 			// 5
    System.out.println(a); 			// 6
    int c = 5;
    int d = ++c; 		// 先加减c+1变为6,后使用,d的值为6
    System.out.println(d); 			// 6
    System.out.println(c); 			// 6
}
2
3
4
5
6
7
8
9
10
11
-- 操作符也是一样的。
++ 和 -- 运算符也不会改变变量的数据类型,举个栗子:
short s = 10;
short t = s ++;         // 仍然是short类型
System.out.println(t);  // 11
2
3
# 3.4 关系运算符
关系运算符也就是比较运算符,主要有以下几种:
| 运算符 | > | < | >= | <= | == | != | 
|---|---|---|---|---|---|---|
| 含义 | 大于 | 小于 | 大于等于 | 小于等于 | 等于 | 不等于 | 
举个栗子:
public static void main(String[] args) {
    int a = 10;
    int b = 3;
    boolean c = a == b;     // 将比较运算的结果赋值给c
    System.out.println(c);          // false
  
    boolean d = a > b;     // 将比较运算的结果赋值给c
    System.out.println(d);          // true
    System.out.println(a != b);     // true
    System.out.println(a < b);      // false
    System.out.println(a >= b);     // true
    System.out.println(a <= b);     // false
    String str1 = "Doubi";
    String str2 = "Doubi";
    System.out.println(str1.equals(str2));	// true
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
关系运算符得到的结果都是 boolean 类型,在后面可以通过使用关系运算符来进行条件的判断。例如身高大于120cm就要买儿童票等场景。
这里需要注意,如果比较两个字符串是否相等,不要使用 == 运算符,要使用 equals() 方法。
因为字符串是引用类型,对于引用类型,== 运算符比较的是两个变量内存地址是否相同,而不是比较对象的内容。
# 3.5 逻辑运算符
# 1 逻辑运算符
主要有以下三种:
| 运算符 | && | || | ! | 
|---|---|---|---|
| 含义 | 与,两边条件同时满足 | 或,两边条件满足一个即可 | 非,取反值,真为假,假为真 | 
举个栗子:
public static void main(String[] args) {
    boolean a = true;
    boolean b = false;
    System.out.println(a && b); 		// false
    System.out.println(a || b); 		// true
    System.out.println(!a); 			  // false
}
2
3
4
5
6
7
逻辑运算符两边必须是 boolean 类型的变量或结果为 boolean 类型的表达式。
逻辑运算符得到的结果还是 boolean 类型,可以赋值给 boolean 类型的变量。
再举个例子:
public static void main(String[] args) {
    int a = 3;
    int b = 12;
    System.out.println(a > 5 && b > 10); 		// false
    System.out.println(a > 5 || b > 10); 		// true
    System.out.println(!(a > 5));           // true
}
2
3
4
5
6
7
8
!(a > 5) 是将 a > 5 的结果取反。 a > 5 结果为 false ,取反则为 true 。
# 2 逻辑运算符的优先级
优先级:! > && > ||
举个栗子:
public static void main(String[] args) {
    int a = 3;
    int b = 12;
    int c = 6;
    int d = 10;
    System.out.println(a > 5 && b > 10 || c > 5 && d > 10);      // false
}
2
3
4
5
6
7
当一个逻辑运算语句中同时包含与、或 、 非运算的时候,会按照优先级进行运算。
所以上面先运算  a > 5 && b > 10,然后运算 c > 5 && d > 10,最后运算 || 两边的结果。
再举个例子:
public static void main(String[] args) {
    boolean a = true;
    boolean b = false;
    boolean result = !a || a && b;
    System.out.println(result);         // false
}
2
3
4
5
6
7
先运算 !a ,结果为 false,然后运算 a && b,结果为 false,最后运算 || 两边的结果,为 false。
如果搞不清楚先后运算的顺序,就用括号 () 括起来,准没错。
# 3 逻辑短路
逻辑短路就是在进行逻辑运算的时候,如果已经能够得到最后的值,就不会再继续进行判断了。
只有 与运算符 && 和 或运算符 || 存在逻辑短路。
举个栗子:
public static void main(String[] args) {
    int num = 10;
    boolean b = num > 20 && num++ < 40;
    System.out.println(num);			 // 10
}
2
3
4
5
在执行上面代码的时候,num > 20 已经不成立了,而 && 运算需要两边都满足,所以后面的条件成不成立都不会影响最终逻辑表达式的结果,所以 && 后面的 num++ < 40 就不会再继续执行了,那么 num 的值就没有 ++,这就是逻辑短路。
同样 || 运算符:
public static void main(String[] args) {
    int num = 10;
    boolean b = num < 20 || num++ > 40;
    System.out.println(num);			// 10
}
2
3
4
5
在执行上面代码的时候,num < 20 已经成立了,|| 运算只要一边满足即可,所以整个逻辑表达式肯定是成立的,所以 || 后面的 num++ > 40 就不会继续执行了,那么 num 的值就没有 ++。
如果不想逻辑短路,可以使用 & 、| 运算符,一个& 或 | 时,左边无论真假,右边都进行运算。
以逻辑与,修改上面的代码:
public static void main(String[] args) {
    int num = 10;
    boolean b = num > 20 & num++ < 40;
    System.out.println(num);        // 11
}
2
3
4
5
使用 & 运算符,就不存在逻辑短路问题,无论左边的运算结果为 true 或 false,右边都会执行运算。
& 、| 运算符用的不多,开发中主要使用 && 和 || 。
# 3.6 位运算符
位运算符在实际的开发中用的不多,在底层代码中会用到,这里了解一下即可。
位运算符允许你直接操作数据的二进制表示形式的各个位。这些运算符可以用于执行各种位级操作,如按位与、按位或、按位取反和按位移动等。位运算符都是对整数类型(如int、long等)进行操作。
建议先学习一下本站的 计算机基础知识 (opens new window) 教程。
1.左移(<<)
- 符号: <<
- 作用: 将操作数的所有位向左移动指定的位数,包括符号位,右侧空出的位用0填充。
举个栗子:
public static void main(String[] args) {
    int i = 1;
    int j = i << 2;
    System.out.println(j);  // 4
}
2
3
4
5
1 的 int 类型,4个字节存储,二进制表示为00000000 00000000 00000000 00000001 ,左移两位,变为 00000000 00000000 00000000 00000100,即为4,左移后,右侧补0。
所以左移n位时候,相当于乘上 2^n 次幂。
- 右移(>>)
- 符号: >>
- 作用: 将操作数的所有位向右移动指定的位数,左侧空出的位使用符号位(正数用0填充,负数用1填充)进行填充。
举个栗子:
public static void main(String[] args) {
    int i = -7;
    int j = i >> 2;
    System.out.println(j);  // -2
}
2
3
4
5
-7 的二进制表示是 11111111 11111111 11111111 11111001,右移两位,右侧被移除两位,变为 11111111 11111111 11111111 11111110,也就是 -2。
- 无符号右移(>>>)
- 作用: 将操作数的所有位向右移动指定的位数(包括符号位),左侧空出的位使用0填充。
举个栗子:
public static void main(String[] args) {
    int i = -7;
    int j = i >>> 2;
    System.out.println(j);  // 1073741822
}
2
3
4
5
-7 的二进制表示是 11111111 11111111 11111111 11111001,右移两位,包括符号位,右侧被移除两位,变为 00111111 11111111 11111111 11111110,也就是 1073741822。
注意:是没有 <<< 运算符的。
- 按位与(&)
- 符号:&
- 作用: 对两个操作数的每一个位执行逻辑与操作,只有两个位都为1时结果才为1,否则为0。
举个栗子:
public static void main(String[] args) {
    int a = 15; // 二进制表示为 1111
    int b = 9;  // 二进制表示为 1001
    // 按位与
    int resultAnd = a & b; // 结果为 9 (0000 1001)
    System.out.println("按位与结果:" + resultAnd);
}
2
3
4
5
6
7
8
每一位进行与操作,并得到结果:
  00000000 00000000 00000000 00001111
& 00000000 00000000 00000000 00001001
= 00000000 00000000 00000000 00001001 = 9
2
3
- 按位或(|)
- 符号:|
- 作用: 对两个操作数的每一个位执行逻辑或操作,只要两个位中有一个为1,结果就为1。
举个栗子:
public static void main(String[] args) {
    int a = 15; // 二进制表示为 1111
    int b = 9;  // 二进制表示为 1001
    // 按位或
    int resultOr = a | b; // 结果为 15 (0000 1111)
    System.out.println("按位或结果:" + resultOr);
}
2
3
4
5
6
7
8
每一位进行或操作,并得到结果:
  00000000 00000000 00000000 00001111
| 00000000 00000000 00000000 00001001
= 00000000 00000000 00000000 00001111 = 15
2
3
- 按位异或(^)
- 符号:^
- 作用: 对两个操作数的每一个位执行异或操作,两个位相同则结果为0,不同则结果为1。
举个栗子:
public static void main(String[] args) {
    int a = 15; // 二进制表示为 1111
    int b = 9;  // 二进制表示为 1001
    // 按位异或
    int resultXor = a ^ b; // 结果为 6 (0000 0110)
    System.out.println("按位异或结果:" + resultXor);
}
2
3
4
5
6
7
8
每一位进行异或操作,并得到结果:
  00000000 00000000 00000000 00001111
| 00000000 00000000 00000000 00001001
= 00000000 00000000 00000000 00000110 = 6
2
3
- 按位取反(~)
- 符号:~
- 作用: 对操作数的每一个位执行取反操作(包括符号位),将每个1变为0,每个0变为1。
举个栗子:
public static void main(String[] args) {
    int a = 15; // 二进制表示为 1111
    
    // 按位取反
    int resultNotA = ~a; // 结果为 -16 (1111 0000)
    System.out.println("按位取反结果:" + resultNotA);
}
2
3
4
5
6
7
每一位进行取反操作,并得到结果:
   00000000 00000000 00000000 00001111
~= 11111111 11111111 11111111 11110000 = -16
2
上面的运算了解一下就好了,实际开发中不涉及底层的,基本用不到。
# 3.7 运算符优先级
上面那么多运算符,如果同时运算的时候,优先级是怎么样的呢?
举个栗子:
public static void main(String[] args) {
    int a = 5;
    int b = 10;
    int c = 15;
    int d = 5;
    int e = 25;
    int f = 2;
    int g = 3;
    int h = 40;
    int i = 1;
    int j = 2;
    boolean m = a + b * c / d - e << f >>> g > 0 && h >> i * j > 0;
    System.out.println(m);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
看着上面的代码,眼不眼晕。在实际的开发中,不要这样写,可读性太差了。
在 Java 中运算符的优先级,按照高到低的顺序如下:
| 优先级 | 运算符 | 分类 | 
|---|---|---|
| 1 | ++,-- | 递增/递减 | 
| 2 | *,/,% | 乘法、除法和取模 | 
| 3 | +,- | 加法和减法 | 
| 4 | <<,>>,>>> | 位移运算 | 
| 5 | <,<=,>,>= | 比较运算 | 
| 6 | ==,!= | 相等和不等运算 | 
| 7 | & | 按位与 | 
| 8 | ^ | 按位异或 | 
| 9 | | | 按位或 | 
| 10 | && | 逻辑与 | 
| 11 | || | 逻辑或 | 
| 12 | ?: | 条件运算符,后面讲解 | 
| 13 | =,+=,-=,*=,/=,%= | 赋值运算符 | 
所以上面的代码,根据优先级括号,应该是这样的:
boolean m = (((a + (b * c / d) - e) << f) >>> g) > 0 && (h >> (i * j)) > 0;
在开发中,不知道优先级高低,添加括号就可以了。
