1. Java运算符全景解析
在Java开发中,运算符就像工匠手中的工具,不同的运算符能完成各种数据处理任务。从基础的加减乘除到复杂的位运算,掌握运算符是写出高效代码的基础功。我见过不少开发者因为对运算符理解不透彻,导致代码出现隐蔽的bug。比如用错自增运算符优先级造成循环逻辑错误,或者混淆位运算与逻辑运算导致条件判断失效。
Java运算符主要分为七大类:算术运算符、关系运算符、位运算符、逻辑运算符、赋值运算符、条件运算符和其他运算符。每种运算符都有特定的使用场景和行为特征,理解它们的细微差别对编写健壮代码至关重要。
提示:运算符优先级是实际开发中最容易踩坑的地方,建议将官方优先级表打印出来贴在工位显眼处。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 基础运算符详解
2.1 算术运算符实战
加减乘除(+ - * /)这些基础运算符看似简单,但在Java中有几个特殊行为需要注意:
- 整数相除会截断小数部分:
5/2=2而不是2.5 - 取模运算(
%)对负数处理遵循"被除数符号决定结果符号"原则:-7%3=-1 - 自增自减运算符的前置(
++i)和后置(i++)区别:java复制int i = 1; System.out.println(i++); // 输出1(先取值后自增) System.out.println(++i); // 输出3(先自增后取值)
浮点数运算有个经典陷阱:
java复制System.out.println(0.1 + 0.2 == 0.3); // false
这是因为浮点数在计算机中无法精确表示,应该使用Math.abs(0.1+0.2-0.3) < 1e-10这样的方式比较。
2.2 关系运算符的坑点
比较运算符(> < >= <= == !=)在对象比较时容易出错:
java复制String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2); // false
System.out.println(s1.equals(s2)); // true
对象应该用equals()比较内容,==比较的是引用地址。对于包装类还有个特殊现象:
java复制Integer a = 127;
Integer b = 127;
System.out.println(a == b); // true
Integer c = 128;
Integer d = 128;
System.out.println(c == d); // false
这是因为Integer缓存了-128到127之间的值。
3. 位运算高级技巧
3.1 位运算符妙用
位运算符(& | ^ ~ << >> >>>)在底层开发、性能优化中很常见:
- 判断奇偶:
(n & 1) == 1 - 交换两个数:
java复制
a ^= b; b ^= a; a ^= b; - 计算绝对值:
java复制int mask = n >> 31; (n ^ mask) - mask;
无符号右移(>>>)与有符号右移(>>)的区别:
java复制int i = -1;
System.out.println(i >> 1); // -1 (符号位保留)
System.out.println(i >>> 1); // 2147483647 (补0)
3.2 位运算性能优化案例
用位运算实现权限控制系统:
java复制// 定义权限常量
int READ = 1 << 0; // 0001
int WRITE = 1 << 1; // 0010
int EXEC = 1 << 2; // 0100
// 赋予权限
int permission = READ | WRITE;
// 检查权限
boolean canWrite = (permission & WRITE) != 0;
// 移除权限
permission &= ~WRITE;
这种方式比用boolean数组节省内存,且位运算速度极快。
4. 逻辑运算符的短路特性
4.1 &&与||的特殊行为
逻辑与(&&)和逻辑或(||)具有短路特性:
java复制if (obj != null && obj.method()) {
// 如果obj为null,后半部分不会执行
}
int i = 0;
if (i == 0 || ++i > 0) {
System.out.println(i); // 输出0(++i未执行)
}
4.2 与位运算符的区别
逻辑运算符(&& || !)与位运算符(& |)的区别:
- 逻辑运算符只能用于boolean类型
- 逻辑运算符有短路特性
- 位运算符可以用于整数类型,逐位运算
一个常见错误:
java复制if (a & b) {...} // 编译错误,应该用&&
5. 赋值运算符的复合用法
5.1 复合赋值运算符
+= -= *= /= %= &= |= ^= <<= >>= >>>=这些复合运算符实际上会做隐式类型转换:
java复制byte b = 10;
b = b + 1; // 编译错误
b += 1; // 正确,等价于 b = (byte)(b + 1)
5.2 三元条件运算符
条件运算符(?:)的嵌套使用要小心可读性:
java复制String result = score > 90 ? "优秀" :
score > 60 ? "及格" : "不及格";
虽然简洁但过度嵌套会降低代码可读性。Java 14开始可以使用switch表达式替代复杂的三元运算。
6. 运算符优先级权威指南
Java运算符优先级从高到低完整列表:
| 优先级 | 运算符 | 结合性 |
|---|---|---|
| 1 | () [] . (方法调用/数组访问/成员访问) |
从左到右 |
| 2 | ! ~ ++ -- + - (强制类型转换) |
从右到左 |
| 3 | * / % |
从左到右 |
| 4 | + - |
从左到右 |
| 5 | << >> >>> |
从左到右 |
| 6 | < <= > >= instanceof |
从左到右 |
| 7 | == != |
从左到右 |
| 8 | & |
从左到右 |
| 9 | ^ |
从左到右 |
| 10 | ` | ` |
| 11 | && |
从左到右 |
| 12 | ` | |
| 13 | ?: |
从右到左 |
| 14 | `= += -= *= /= %= &= | = ^= <<= >>= >>>=` |
容易混淆的优先级案例:
java复制int x = 5 + 2 * 3; // 11 不是21
boolean b = true || false && false; // true (&&优先级高于||)
7. Java新特性中的运算符变化
7.1 switch表达式
Java 12引入的switch表达式可以替代部分三元运算:
java复制String result = switch (score) {
case 90 -> "优秀";
case 60 -> "及格";
default -> "不及格";
};
7.2 文本块中的运算符
Java 15文本块与字符串运算符的结合:
java复制String html = """
<html>
<body>
<p>Hello, %s!</p>
</body>
</html>
""".formatted(name);
8. 运算符性能优化实战
8.1 算术运算优化
- 用移位代替乘除2的幂次:
java复制int x = y << 3; // 相当于 y * 8 int z = y >> 2; // 相当于 y / 4 - 用
&判断2的幂:java复制(n & (n - 1)) == 0
8.2 避免自动装箱拆箱
包装类的运算会导致频繁拆箱装箱:
java复制Integer sum = 0;
for (int i = 0; i < 1000; i++) {
sum += i; // 每次+=都会拆箱和装箱
}
应该改用基本类型int。
9. 常见面试题解析
9.1 运算符优先级题目
java复制int i = 5;
int result = i++ + ++i * i-- - --i;
System.out.println(result); // 输出?
分步解析:
- i++:取5,i变为6
- ++i:i先变为7,取7
- i--:取7,i变为6
- --i:i先变为5,取5
- 计算:5 + 7 * 7 - 5 = 5 + 49 - 5 = 49
9.2 位运算题目
交换两个变量的值,不使用临时变量:
java复制a ^= b;
b ^= a;
a ^= b;
原理:利用a ^ b ^ b = a的特性。
10. 开发中的最佳实践
- 复杂表达式要加括号明确优先级,即使知道优先级规则
- 避免在单个表达式中对同一变量多次自增自减
- 浮点数比较要使用误差范围,不要直接==
- 位运算要加注释说明意图
- 使用包装类时注意自动拆箱导致的NPE风险
- 三元运算符嵌套不要超过两层
重要:在团队协作中,如果运算符使用可能导致歧义,宁可拆分成多行代码也不要追求过度简洁。代码可读性永远比炫技更重要。
