运算符
运算符的分类
算数运算
| 符号 | 含义 |
|---|---|
+ |
加 |
- |
减 |
* |
乘 |
/ |
除 |
% |
取模(求余数) |
++ |
自增 |
-- |
自减 |
关系运算
| 符号 | 含义 |
|---|---|
== |
等于 |
!= |
不等于 |
> |
大于 |
< |
小于 |
>= |
大于等于 |
<= |
小于等于 |
逻辑运算
| 符号 | 含义 |
|---|---|
&& |
与(AND) |
∣∣ |
或(OR) |
! |
非(NOT) |
位运算
| 符号 | 含义 |
|---|---|
& |
按位与 |
∣ |
按位或 |
^ |
按位异或 |
~ |
按位取反 |
<< |
按位左移 |
>> |
按位右移(补符号位) |
>>> |
无符号按位右移(补 0) |
赋值运算
| 符号 | 含义 |
|---|---|
= |
简单赋值 |
+= |
加后赋值 |
-= |
减后赋值 |
*= |
乘后赋值 |
/= |
除后赋值 |
%= |
取模后赋值 |
<<= |
左移后赋值 |
>>= |
右移后赋值 |
&= |
按位与后赋值 |
^= |
按位异或后赋值 |
∣= |
按位或后赋值 |
条件运算符(三元运算符)
| 符号 | 格式 | 含义 |
|---|---|---|
?: |
var = condition ? exp1 : exp2 |
如果 condition 为真返回 exp1 的值,否则返回 exp2 的值 |
instanceof 运算符
| 符号 | 格式 | 含义 |
|---|---|---|
instanceof |
result = obj instanceof clazz |
检查 obj 对象是否是特定类 clazz 或 clazz 子类的实例 |
运算符的优先级
| 优先级 | 运算符 | 类型 | 示例 | 结合性 |
|---|---|---|---|---|
| 1 | .、[]、() |
访问、调用 | obj.method() |
从左到右 |
| 2 | !、~、+、-、++ 、-- |
一元运算 | -a、a++ |
从右到左 |
| 3 | *、/、% |
算术运算(乘除模) | a * b |
从左到右 |
| 4 | +、- |
算术运算(加减) | a + b |
从左到右 |
| 5 | <<、>>、>>> |
位移运算 | a << 2 |
从左到右 |
| 6 | <、<=、>、>=、instanceof |
比较运算 | a > b 、x instanceof Y |
从左到右 |
| 7 | ==、!= |
相等性比较 | a == b |
从左到右 |
| 8 | & |
位与 | a & b |
从左到右 |
| 9 | ^ |
位异或 | a ^ b |
从左到右 |
| 10 | ∣ |
位或 | a ∣ b |
从左到右 |
| 11 | && |
逻辑与(短路) | a && b |
从左到右 |
| 12 | ∣∣ |
逻辑或(短路) | a ∣∣ b |
从左到右 |
| 13 | ?: |
条件(三元)运算符 | a > b ? x : y |
从右到左 |
| 14 | =、+=、-=、*=、/=、%=、&=、|=、^=、~=、<<= >>=、>>>= |
赋值运算 | a = b, a += 3 |
从右到左 |
| 15 | , |
逗号运算 | a = 1, b = 2 |
从右到左 |
🔔 记忆口诀:
括号调用先发言,单目正反紧相连。
乘除模后再加减,移位排名略靠前。
大小比较随其后,真假对比肩并肩。
先位与,再异或,然后才能是位或。
逻辑与,逻辑或,三目赋值在加逗。
结合性
运算符的结合性(Associativity) 决定了当一个表达式中出现多个相同优先级的运算符时,它们的执行顺序是从左到右(左结合)还是从右到左(右结合)的。结合性只在运算符优先级相同时生效,优先级不同时则按优先级高低执行。
左结合(Left-associative):按从左到右的顺序计算。
int a = 5, b = 2, c = 3;
int sum = a + b - c;
// 等价于
int sum = (a + b) - c;
boolean a = false, b = true, c = false, d = true, e = false;
boolean bool = a && b || c && d || e;
// 等价于
boolean bool = ((a && b) || (c && d)) || e;
右结合(Right-associative):按从右到左的顺序计算。
int a = 5, b = 2, c = 3;
a = b = 5;
// 等价于
a = (b = 5)
boolean a = false, b = true, c = false, d = true, e = false;
boolean bool = a ? b : c ? d : e;
// 等价于
boolean bool = a ? b : (c ? d : e);
余数的正负
余数的正负只和表达式中的左半部分有关,例如:
// 结果为 1
int a = 7 % 3;
// 结果依旧为 1
int a = 7 % -3;
// 结果为 -1
int a = -7 % 3;
运算中的 a++ 与 ++a
虽然 a++ 和 ++a 都是自增运算符,但是执行时机并不同。
a在前的后置自增a++:先使用变量a的当前值参与运算,然后再将a的值加 1。a在后的前置自增++a:先将变量a的值加 1,然后再使用增加后的值参与运算。
示例:
int a = 5;
// 因为 a 在前,所以先使用 a 的当前值参与运算
// 结果是:b 先变成是 5,然后 a 再变成 6
int b = a++;
int a = 5;
// 因为 a 在后,所以先将变量 a 的值加 1,在参与运算
// 结果是:a 先变成是 6,然后 b 才变成 6
int b = ++a;
复杂点的例子:
int m = 1;
int n = 1;
int sum = m++ + ++n - n-- - --m + n-- - --m;
// m++ 先使用 m 的当前值参与运算,再将 m 加 1,此时运算值 = 1,m = 2
// ++n 先将 n 加 1,然后再使用增加后的值参与运算,此时运算值 = 2,n = 2
// n-- 先使用 n 的当前值参与运算,再将 n 减 1,此时运算值 = 2,n = 1
// --m 先将 m 减 1,然后再使用减少后的值参与运算,此时运算值 = 1,m = 1
// n-- 先使用 n 的当前值参与运算,再将 n 减 1,此时运算值 = 1,n = 0
// --m 先将 m 减 1,然后再使用减少后的值参与运算,此时运算值 = 0,m = 0
// 最终结果:
// sum = 1 + 2 - 2 - 1 + 1 - 0 = 1
// m = 0
// n = 0
交换两个变量的值
使用临时变量
使用临时变量,最常用,最容易理解,推荐使用。
public class Main {
public static void main(String[] args) {
int a = 5, b = 10;
int temp = a;
a = b;
b = temp;
System.out.println("a = " + a + ", b = " + b);
}
}
使用算数运算
可能会溢出,不推荐使用。
class Main {
public static void main(String[] args) {
int a = 5, b = 10;
a = a + b;
b = a - b;
a = a - b;
System.out.println("a = " + a + ", b = " + b);
}
}
使用位运算
仅适用于整数,不推荐使用。
public class Main {
public static void main(String[] args) {
int a = 5, b = 10;
a = a ^ b;
b = a ^ b;
a = a ^ b;
System.out.println("a = " + a + ", b = " + b);
}
}
德摩根定律
- 第一定律:
NOT (A AND B)等价于(NOT A) OR (NOT B) - 第二定律:
NOT (A OR B)等价于(NOT A) AND (NOT B)
逻辑短路
在使用逻辑运算符(&& 和 ||)时,如果能够根据第一个操作数确定整个表达式的结果,就不再计算第二个操作数的现象。具体来说:
- 逻辑与(
&&)短路:当第一个操作数为false时,整个表达式结果必定为false,此时程序不会计算第二个操作数。 - 逻辑或(
||)短路:当第一个操作数为true时,整个表达式结果必定为true,此时不会计算第二个操作数。
这种特性在实际编程中非常有用,可以避免不必要的计算,提高代码执行效率,也可以防止空指针异常。
示例:
int[] a = null;
// 单独使用会抛出 NullPointerException 异常
int len = a.length;
// 如果 a != null 计算为 false,那么 a.length 就不会执行
if (a != null && a.length > 0) {
System.out.println("&& 短路");
}
// 如果 a == null 计算为 true,那么 a.length 就不会执行
if (a == null || a.length <= 0) {
System.out.println("|| 短路");
}
如果在某些情况下可能需要避免短路的发生,那么可以使用单个的 & 和 | 路基运算符,它们总是会计算两个操作数。
当条件表达式中包含必须执行的副作用代码时(比如修改变量、I/O 操作等),短路就可能会跳过后续操作从而导致状态不一致。
示例:
int[] a = null;
// 即使 a != null 计算为 false,但是 a.length 依旧会执行,从而抛出 NullPointerException 异常
if (a != null & a.length > 0) {
System.out.println("&& 短路");
}
// 即使 a 为 null,但是 a.length 依旧会执行,从而抛出 NullPointerException 异常
if (a == null | a.length <= 0) {
System.out.println("|| 短路");
}

Comments NOTHING