第 7 章:运算符详解
学习目标
- 掌握算术、关系、逻辑、位、三元运算符
- 理解运算符优先级
- 学会用位运算做企业级权限设计
- 避免运算符的常见坑
一、Java 运算符全景图
二、算术运算符
2.1 基本算术运算
java
int a = 10, 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(取余/取模)
double x = 10.0, y = 3.0;
System.out.println(x / y); // 3.3333333333333335(浮点除法)2.2 自增自减
java
int i = 5;
System.out.println(i++); // 5(先取值后自增)
System.out.println(++i); // 7(先自增后取值)
System.out.println(i--); // 7(先取值后自减)
System.out.println(--i); // 5(先自减后取值)自增自减的坑
javaint i = 5; int j = i++ + ++i; // ❌ 复杂表达式,结果未定义 // 不同 JVM 实现可能给出不同结果企业级代码中:
i++/++i单独使用,不要混进复杂表达式。
2.3 整数除法的陷阱
java
int a = 5, b = 2;
System.out.println(a / b); // 2(不是 2.5)
System.out.println(a / (double) b); // 2.5(强转才能得到小数)
// 经典场景:计算平均分
int total = 87, count = 10;
double average = total / count; // ❌ 8.0(错误:整数除法)
double correct = total / (double) count; // ✅ 8.7三、关系运算符
关系运算符返回 boolean 值(true / false)。
java
int a = 10, b = 20;
System.out.println(a > b); // false
System.out.println(a < b); // true
System.out.println(a >= b); // false
System.out.println(a <= b); // false
System.out.println(a == b); // false(值相等)
System.out.println(a != b); // true
==比较包装类的陷阱javaInteger x = 100, y = 100; System.out.println(x == y); // true(缓存池) Integer m = 200, n = 200; System.out.println(m == n); // false(不在缓存池) System.out.println(m.equals(n)); // true(值相等)永远用
equals比较包装类!
四、逻辑运算符
4.1 三种逻辑运算符
java
boolean a = true, b = false;
System.out.println(a && b); // false(逻辑与 AND)
System.out.println(a || b); // true(逻辑或 OR)
System.out.println(!a); // false(逻辑非 NOT)
System.out.println(a ^ b); // true(逻辑异或 XOR)4.2 短路求值(关键特性)
&& 和 || 有短路求值特性:
java
// && 短路:左边为 false,右边不执行
int x = 0;
if (x != 0 && 10 / x > 1) { // x != 0 为 false,10/x 不执行,避免除零
System.out.println("ok");
}
// || 短路:左边为 true,右边不执行
if (x == 0 || 10 / x > 1) { // x == 0 为 true,10/x 不执行
System.out.println("ok");
}短路求值的实际用途
- 避免除零:
x != 0 && 10/x > 1- 避免空指针:
obj != null && obj.method()- 性能优化:把昂贵判断放右边
4.3 & 和 | vs && 和 ||
java
// & 和 | 没有短路
int x = 0;
if (x != 0 & 10 / x > 1) { // ❌ 仍然会执行 10/x,抛 ArithmeticException
System.out.println("ok");
}| 运算符 | 类型 | 短路 | 用途 |
|---|---|---|---|
&& | 逻辑与 | ✅ | 布尔逻辑(推荐) |
& | 位与 / 不短路逻辑与 | ❌ | 位运算 / 不需要短路时 |
|| | 逻辑或 | ✅ | 布尔逻辑(推荐) |
| | 位或 / 不短路逻辑或 | ❌ | 位运算 / 不需要短路时 |
五、位运算符
位运算符直接操作二进制位,速度极快。
5.1 基本位运算
java
int a = 5; // 0101
int b = 3; // 0011
System.out.println(a & b); // 1(0101 & 0011 = 0001)按位与:两位都是 1 才为 1
System.out.println(a | b); // 7(0101 | 0011 = 0111)按位或:任一位是 1 即为 1
System.out.println(a ^ b); // 6(0101 ^ 0011 = 0110)按位异或:不同为 1
System.out.println(~a); // -6(~00000101 = 11111010)按位取反5.2 移位运算
java
int a = 8; // 00001000
System.out.println(a << 1); // 16(左移 1 位,相当于 × 2)
System.out.println(a << 2); // 32(左移 2 位,相当于 × 4)
System.out.println(a >> 1); // 4(右移 1 位,相当于 / 2)
System.out.println(a >> 2); // 2(右移 2 位,相当于 / 4)
// >>> 无符号右移:高位补 0
int b = -8;
System.out.println(b >> 1); // -4(保留符号位)
System.out.println(b >>> 1); // 2147483644(高位补 0,巨大正数)5.3 实战案例:企业级权限设计
位运算的经典应用是位掩码权限设计:
java
/**
* 权限位掩码
* 每个权限占一个 bit 位
*/
public class Permission {
public static final int READ = 1 << 0; // 0001 = 1
public static final int WRITE = 1 << 1; // 0010 = 2
public static final int DELETE = 1 << 2; // 0100 = 4
public static final int ADMIN = 1 << 3; // 1000 = 8
// 用户权限:可以读 + 写
int userPerm = Permission.READ | Permission.WRITE; // 0001 | 0010 = 0011 = 3
// 检查是否有读权限(按位与)
boolean canRead = (userPerm & Permission.READ) != 0; // true
boolean canDelete = (userPerm & Permission.DELETE) != 0; // false
// 添加权限(按位或)
userPerm |= Permission.DELETE; // 现在 userPerm = 0111
// 移除权限(按位与 + 按位取反)
userPerm &= ~Permission.DELETE; // 现在 userPerm = 0011
// 切换权限(按位异或)
userPerm ^= Permission.WRITE; // 现在 userPerm = 0001
}权限位掩码的图解:
这种设计 30 年来一直是 Unix/Linux 文件系统的标准方式(rwxrwxrwx)。
5.4 实战案例:交换两个整数(不用临时变量)
java
int a = 5, b = 10;
// 用异或交换
a = a ^ b; // a = 5^10
b = a ^ b; // b = (5^10)^10 = 5
a = a ^ b; // a = (5^10)^5 = 10
// 结果:a=10, b=5
// 或者用加减法(有溢出风险)
a = a + b;
b = a - b;
a = a - b;六、赋值运算符
java
int x = 10;
x += 5; // 等价于 x = x + 5 = 15
x -= 3; // 等价于 x = x - 3 = 12
x *= 2; // 等价于 x = x * 2 = 24
x /= 4; // 等价于 x = x / 4 = 6
x %= 4; // 等价于 x = x % 4 = 2
// 位赋值
int flags = 0;
flags |= 1; // 等价于 flags = flags | 1
flags &= ~2; // 等价于 flags = flags & ~2
flags ^= 4; // 等价于 flags = flags ^ 4七、三元运算符
java
int age = 18;
String status = (age >= 18) ? "成年" : "未成年";
System.out.println(status); // 成年
// 嵌套三元(不推荐,可读性差)
String level = (score >= 90) ? "A" : (score >= 60) ? "B" : "C";八、字符串拼接运算符
+ 在 Java 中有双重身份:
java
// 数值加法
System.out.println(1 + 2); // 3
// 字符串拼接
System.out.println("Hello" + "World"); // HelloWorld
// 混合运算:从左到右
System.out.println(1 + 2 + "3"); // 33(先 1+2=3,再 3+"3"="33")
System.out.println("1" + 2 + 3); // 123(先 "1"+2="12",再 "12"+3="123")企业级代码:循环中频繁字符串拼接用
StringBuilder,不要用+。
九、instanceof 运算符
java
Object obj = "Hello";
if (obj instanceof String) { // 类型检查
String str = (String) obj; // 类型转换
System.out.println(str.toUpperCase());
}
// Java 16+ 模式匹配
if (obj instanceof String str) { // 直接绑定变量
System.out.println(str.toUpperCase());
}十、运算符优先级
从高到低(建议用括号避免混淆):
| 优先级 | 运算符 |
|---|---|
| 1 | () [] . |
| 2 | ++ -- ~ ! |
| 3 | * / % |
| 4 | + - |
| 5 | << >> >>> |
| 6 | < <= > >= instanceof |
| 7 | == != |
| 8 | & |
| 9 | ^ |
| 10 | | |
| 11 | && |
| 12 | || |
| 13 | ? : |
| 14 | = += -= *= ... |
java
// 复杂表达式:靠优先级不如靠括号
int result = 2 + 3 * 4; // 14(先 3*4=12,再 +2)
int clear = 2 + (3 * 4); // 14(加括号更清晰)
// ❌ 反例
if (a > 0 && b < 10 || c == 5) { // 不加括号可读性差
// ...
}
// ✅ 正例
if ((a > 0 && b < 10) || c == 5) { // 明确优先级
// ...
}十一、完整代码示例
java
/**
* 运算符综合示例
*
* 配套文档:第 7 章 运算符详解
* 运行方式:javac OperatorDemo.java && java OperatorDemo
*/
public class OperatorDemo {
// 权限位掩码常量
public static final int PERM_READ = 1 << 0; // 1
public static final int PERM_WRITE = 1 << 1; // 2
public static final int PERM_DELETE = 1 << 2; // 4
public static final int PERM_ADMIN = 1 << 3; // 8
public static void main(String[] args) {
// ============ 1. 算术运算符 ============
System.out.println("=== 算术 ===");
int a = 10, b = 3;
System.out.println("a + b = " + (a + b)); // 13
System.out.println("a - b = " + (a - b)); // 7
System.out.println("a * b = " + (a * b)); // 30
System.out.println("a / b = " + (a / b)); // 3(整数除法)
System.out.println("a % b = " + (a % b)); // 1
// ============ 2. 自增自减 ============
System.out.println("\n=== 自增自减 ===");
int i = 5;
System.out.println("i++ = " + (i++)); // 5,i 变 6
System.out.println("++i = " + (++i)); // 7,i 变 7
System.out.println("i-- = " + (i--)); // 7,i 变 6
System.out.println("--i = " + (--i)); // 5,i 变 5
// ============ 3. 关系与逻辑 ============
System.out.println("\n=== 关系与逻辑 ===");
int age = 20;
boolean isAdult = age >= 18 && age <= 60; // 短路与
boolean isMinor = age < 18 || age > 60;
System.out.println("是否成年: " + isAdult);
System.out.println("是否未成年或老年: " + isMinor);
// ============ 4. 位运算 ============
System.out.println("\n=== 位运算 ===");
System.out.println("5 & 3 = " + (5 & 3)); // 1
System.out.println("5 | 3 = " + (5 | 3)); // 7
System.out.println("5 ^ 3 = " + (5 ^ 3)); // 6
System.out.println("8 << 1 = " + (8 << 1)); // 16
// ============ 5. 实战:位掩码权限 ============
System.out.println("\n=== 权限系统 ===");
int userPerm = PERM_READ | PERM_WRITE; // 用户有读 + 写权限
System.out.println("有读权限? " + ((userPerm & PERM_READ) != 0)); // true
System.out.println("有写权限? " + ((userPerm & PERM_WRITE) != 0)); // true
System.out.println("有删除权限? " + ((userPerm & PERM_DELETE) != 0)); // false
userPerm |= PERM_DELETE; // 添加删除权限
System.out.println("添加删除后: " + Integer.toBinaryString(userPerm));
userPerm &= ~PERM_WRITE; // 移除写权限
System.out.println("移除写后: " + Integer.toBinaryString(userPerm));
// ============ 6. 三元运算符 ============
System.out.println("\n=== 三元 ===");
int score = 85;
String result = score >= 60 ? "及格" : "不及格";
System.out.println("成绩: " + result);
// ============ 7. 字符串拼接陷阱 ============
System.out.println("\n=== 字符串拼接 ===");
System.out.println(1 + 2 + "3"); // 33
System.out.println("1" + 2 + 3); // 123
}
}运行结果:
=== 算术 ===
a + b = 13
a - b = 7
a * b = 30
a / b = 3
a % b = 1
=== 自增自减 ===
i++ = 5
++i = 7
i-- = 7
--i = 5
=== 关系与逻辑 ===
是否成年: true
是否未成年或老年: false
=== 位运算 ===
5 & 3 = 1
5 | 3 = 7
5 ^ 3 = 6
8 << 1 = 16
=== 权限系统 ===
有读权限? true
有写权限? true
有删除权限? false
添加删除后: 111
移除写后: 101
=== 三元 ===
成绩: 及格
=== 字符串拼接 ===
33
123十二、常见面试题
Q1: i++ 和 ++i 哪个更快?
理论上 ++i 稍快(无需保存旧值),但实际 JVM 优化后没有差别。不要为了性能选择,要看语义。
Q2: & 和 && 的区别?
| 运算符 | 类型 | 短路 |
|---|---|---|
&& | 布尔逻辑 | ✅ |
& | 位运算 / 不短路布尔 | ❌ |
Q3: 为什么位运算更快?
CPU 直接操作寄存器,无方法调用、无类型检查。但可读性差,只在性能敏感场景用(如权限系统、加密算法)。
Q4: ^ 异或有什么妙用?
- 交换两数:不用临时变量
- 找出唯一出现一次的数(其他数都出现两次)
- 简单加密:XOR 加密
十三、本章小结
| 要点 | 关键 |
|---|---|
| 算术运算 | 注意整数除法会截断,强转才能得到小数 |
| 关系运算 | 比较包装类用 equals,不要用 == |
| 逻辑运算 | && || 有短路,& | 没有 |
| 位运算 | 速度极快,适合权限掩码、加密、底层算法 |
| 字符串拼接 | + 从左到右运算,循环中用 StringBuilder |
| 优先级 | 复杂表达式用括号,不要背优先级 |
动手练习
练习 1:基础题
写一个温度转换器:
java
// 摄氏度 → 华氏度:F = C × 9/5 + 32
// 华氏度 → 摄氏度:C = (F - 32) × 5/9
double celsius = 25;
double fahrenheit = ???;
System.out.println(celsius + "°C = " + fahrenheit + "°F");练习 2:进阶题
用位掩码实现一个文件权限系统:
java
// 权限:READ(1) / WRITE(2) / EXECUTE(4)
// 给用户赋予读 + 执行权限,检查并打印他能做什么练习 3:挑战题
不用临时变量,用位运算交换两个 int 的值,并解释每一步为什么能交换。
推荐阅读
- 📖 《Java 核心技术》卷 1 第 3.4-3.5 节 — 运算符详解
- 📖 《码出高效》第 4 章 — 位运算实战
- 🌐 Java 位运算速查 — 菜鸟教程
下一章:第 8 章:流程控制 — 分支结构 →