在 Java 开发中,运算符是构建代码逻辑的 "基本零件"------ 无论是简单的数值计算,还是复杂的条件判断、位操作,都离不开运算符的灵活运用。但很多开发者在实际开发中常踩 "整数除法丢精度"" 逻辑短路漏执行 ""优先级混淆算错值" 等坑。本文将系统拆解算术、赋值、比较、逻辑、位 5 大类运算符,结合实战代码与优先级可视化图,帮你从 "会用" 到 "用对"。
编辑
一、算术运算符:数值计算的核心(7 种)
算术运算符用于整数、浮点数的加减乘除等计算,其中整数除法 和取模是高频坑点。
1. 基础用法与代码示例
运算符 | 名称 | 作用 | 示例(a=10, b=3) | 结果 |
---|---|---|---|---|
+ | 加法 | 数值相加 / 字符串拼接 | a+b / "a"+b | 13 / "103" |
- | 减法 | 数值相减 | a-b | 7 |
* | 乘法 | 数值相乘 | a*b | 30 |
/ | 除法 | 数值相除(整数取整) | a/b / 10.0/3 | 3 / 3.333... |
% | 取模(余) | 求被除数除以除数的余数 | a%b / -10%3 | 1 / 2 |
++ | 自增 | 数值 + 1(前置先加后用,后置先用后加) | ++a / a++ | 11 / 10 |
-- | 自减 | 数值 - 1(同自增规则) | --a / a-- | 9 / 10 |
csharp
public class ArithmeticDemo {
public static void main(String[] args) {
// 1. 整数除法坑点:结果自动取整,不会四舍五入
int num1 = 10;
int num2 = 3;
System.out.println("整数除法:" + num1 / num2); // 输出3(而非3.333)
System.out.println("浮点数除法:" + (double)num1 / num2); // 输出3.3333333333333335
// 2. 取模正负性:结果符号与被除数一致
System.out.println("正被除数取模:" + 10 % 3); // 1
System.out.println("负被除数取模:" + (-10) % 3); // -1(重点:不是2)
// 3. 自增自减的前置/后置差异
int a = 5;
int b = ++a; // 先加后用:a=6,b=6
int c = a--; // 先用后减:c=6,a=5
System.out.println("a=" + a + ", b=" + b + ", c=" + c); // a=5, b=6, c=6
// 4. 字符串拼接优先级(+遇字符串则拼接)
System.out.println("字符串拼接1:" + 10 + 3); // "103"
System.out.println("字符串拼接2:" + (10 + 3)); // "13"
}
}
2. 避坑指南
- 整数除法必须先转浮点数:若需精确结果,至少将一个操作数转为
double
或float
; - 取模别忽略符号:判断奇偶性时用
num % 2 == 0
(负数也适用),避免依赖正余数; - 自增自减别嵌套:
a++ + ++a
这类表达式可读性差且易算错,建议拆分为多行。
二、赋值运算符:变量赋值的简化技巧(12 种)
赋值运算符用于将右侧值赋给左侧变量,复合赋值运算符(+=、-= 等)是简化代码的核心,且隐含 "自动类型转换" 特性。
1. 基础用法与代码示例
运算符 | 名称 | 作用 | 等效表达式 | |||
---|---|---|---|---|---|---|
= | 基本赋值 | 将右侧值赋给左侧变量 | a = b | |||
+= | 加后赋值 | 左侧 + 右侧后赋给左侧 | a = (类型) a + b | |||
-= | 减后赋值 | 左侧 - 右侧后赋给左侧 | a = (类型) a - b | |||
*= | 乘后赋值 | 左侧 * 右侧后赋给左侧 | a = (类型) a * b | |||
/= | 除后赋值 | 左侧 / 右侧后赋给左侧 | a = (类型) a /b | |||
%= | 模后赋值 | 左侧 % 右侧后赋给左侧 | a = (类型) a % b | |||
&= | 与后赋值 | 左侧 & 右侧后赋给左侧 | a = a & b | |||
= | 或后赋值 | 左侧 | 右侧后赋给左侧 | a = a | b | |
^= | 异或后赋值 | 左侧 ^ 右侧后赋给左侧 | a = a ^ b | |||
>>= | 右移后赋值 | 左侧 >> 右侧后赋给左侧 | a = a >> b | |||
<<= | 左移后赋值 | 左侧 << 右侧后赋给左侧 | a = a << b | |||
>>>= | 无符号右移赋值 | 左侧 >>> 右侧后赋给左侧 | a = a >>> b |
csharp
public class AssignmentDemo {
public static void main(String[] args) {
// 1. 复合赋值的隐含类型转换(重点坑点)
byte num = 10;
// num = num + 5; // 报错:int不能转byte
num += 5; // 正常执行:等效于 num = (byte)(num + 5),结果15
// 2. 赋值运算符的右结合性(从右向左计算)
int a, b, c;
a = b = c = 20; // 先c=20,再b=20,最后a=20
System.out.println("a=" + a + ", b=" + b + ", c=" + c); // 20,20,20
// 3. 位运算赋值的简化(后续位运算符详解)
int flag = 1; // 二进制 0001
flag |= 2; // 0001 | 0010 = 0011(3)
System.out.println("位或赋值后:" + flag); // 3
}
}
2. 避坑指南
- 复合赋值自动转类型:对
byte
、short
、char
等小类型,+=
会自动强转,避免直接赋值的编译错误; - 别混淆 "=" 和 "==":赋值是 "=",比较相等是 "==",写条件判断时极易手抖写错。
三、比较运算符:条件判断的依据(6 种)
比较运算符用于判断两个值的关系,结果始终是 boolean 类型(true/false),常与 if、while 等流程控制结合使用。
1. 基础用法与代码示例
运算符 | 名称 | 作用 | 示例(a=10, b=3) | 结果 |
---|---|---|---|---|
== | 等于 | 判断两个值是否相等 | a==b / 10==10.0 | false / true |
!= | 不等于 | 判断两个值是否不相等 | a!=b | true |
大于 | 判断左侧是否大于右侧 | a>b | true | |
< | 小于 | 判断左侧是否小于右侧 | a<b | false |
>= | 大于等于 | 判断左侧是否大于等于右侧 | a>=b | true |
<= | 小于等于 | 判断左侧是否小于等于右侧 | a<=b | false |
csharp
public class ComparisonDemo {
public static void main(String[] args) {
// 1. 基本类型 vs 引用类型的"=="差异
int num1 = 10;
int num2 = 10;
System.out.println("基本类型==:" + (num1 == num2)); // true(值相等)
String str1 = new String("java");
String str2 = new String("java");
System.out.println("引用类型==:" + (str1 == str2)); // false(地址不同)
System.out.println("引用类型equals:" + str1.equals(str2)); // true(内容相等)
// 2. 浮点数比较的坑(避免直接用==)
double d1 = 0.1;
double d2 = 0.2;
double sum = d1 + d2;
System.out.println("0.1+0.2==0.3?" + (sum == 0.3)); // false(精度丢失)
// 正确写法:判断差值小于极小值
System.out.println("浮点数比较正确写法:" + (Math.abs(sum - 0.3) < 1e-9)); // true
}
}
2. 避坑指南
- 引用类型别用 "==" 比内容:
==
比较引用地址,内容比较用equals
(注意 null 判断,避免空指针); - 浮点数禁止直接 "==":由于二进制存储精度问题,需通过
Math.abs(差值) < 1e-9
判断相等; - 比较表达式不能连写:
1 < x < 3
在 Java 中非法(会先算 1<x 得到 boolean,再用 boolean 和 3 比较),需拆分为x>1 && x<3
。
四、逻辑运算符:布尔值的组合判断(6 种)
逻辑运算符用于连接多个 boolean 表达式,核心是短路特性(影响代码执行顺序),分为 "短路型" 和 "非短路型"。
1. 基础用法与代码示例
运算符 | 名称 | 作用(左右为 boolean 表达式) | 短路特性 | 示例(a=true, b=false) | 结果 | ||||||
---|---|---|---|---|---|---|---|---|---|---|---|
&& | 逻辑与 | 左右都为 true 则 true | 短路(左 false 则不执行右) | a&&b / b&&(1/0==0) | false /false(不报错) | ||||||
逻辑或 | 左右有一个 true 则 true | 短路(左 true 则不执行右) | a | b / a | (1/0==0) | true /true(不报错) | |||||
! | 逻辑非 | 取反(单目运算符) | 无 | !a | false | ||||||
& | 逻辑与(非短路) | 左右都为 true 则 true | 非短路(必执行右) | a&b / b&(1/0==0) | false / 报错(执行了 1/0) | ||||||
逻辑或(非短路) | 左右有一个 true 则 true | 非短路(必执行右) | a | b / a | (1/0==0) | true / 报错(执行了 1/0) | |||||
逻辑异或 | 左右不同则 true,相同则 false | 无 | a^b / a^a | true / false |
arduino
public class LogicDemo {
public static void main(String[] args) {
// 1. 短路特性的实际影响(性能+执行顺序)
int num = 10;
boolean result1 = (num > 20) && (num++ > 5); // 左false,右不执行
System.out.println("短路与后num:" + num); // 10(num++未执行)
boolean result2 = (num < 20) || (num++ > 5); // 左true,右不执行
System.out.println("短路或后num:" + num); // 10(num++未执行)
// 2. 非短路与的坑(即使左false,右仍执行)
boolean result3 = (num > 20) & (num++ > 5);
System.out.println("非短路与后num:" + num); // 11(num++执行了)
// 3. 逻辑非的优先级(高于算术/比较运算符)
boolean flag = !(10 > 5);
System.out.println("逻辑非结果:" + flag); // false
}
}
2. 避坑指南
- 优先用短路运算符:
&&
和||
可避免无效计算(如右侧有耗时操作或可能报错的代码); - 非短路仅用特殊场景:需强制执行右侧代码时(如同时判断条件 + 更新状态)才用
&
/|
; - 加括号明确优先级:逻辑运算符优先级低(低于比较运算符),复杂表达式必须加括号,如
(a>b) && (c<d)
。
五、位运算符:二进制级别的高效操作(7 种)
位运算符直接操作整数的二进制位,执行效率极高,常用于权限控制、数据压缩等场景,但上手难度较高。
1. 基础概念:二进制位的表示
Java 中整数以补码形式存储,以int
(32 位)为例:
- 正数:原码 = 反码 = 补码(如 10 的二进制:
00000000 00000000 00000000 00001010
); - 负数:反码 = 原码除符号位外取反,补码 = 反码 + 1(如 - 10 的补码:
11111111 11111111 11111111 11110110
)。
2. 基础用法与代码示例
运算符 | 名称 | 作用(操作二进制位) | 示例(a=10=00001010, b=3=00000011) | 结果(二进制→十进制) | ||
---|---|---|---|---|---|---|
& | 位与 | 同位都为 1 则 1,否则 0 | a&b = 00000010 | 2 | ||
位或 | 同位有 1 则 1,否则 0 | a | b = 00001011 | 11 | ||
位异或 | 同位不同则 1,相同则 0 | a^b = 00001001 | 9 | |||
~ | 位非 | 所有位取反(单目运算符) | ~a = 11110101(补码) | -11 | ||
<< | 左移 | 整体左移 n 位,右补 0 | a<<2 = 00101000 | 40(10*2²) | ||
>> | 算术右移 | 整体右移 n 位,左补符号位 | a>>2 = 00000010(正数) | 2(10/2²) | ||
>>> | 无符号右移 | 整体右移 n 位,左补 0(忽略符号) | (-10)>>>28 = 00001111 | 15 |
csharp
public class BitDemo {
public static void main(String[] args) {
// 1. 位运算的经典场景:权限控制(每一位代表一个权限)
int READ = 1 << 0; // 0001(读权限)
int WRITE = 1 << 1; // 0010(写权限)
int DELETE = 1 << 2; // 0100(删权限)
int userPerm = READ | WRITE; // 0011(拥有读+写权限)
// 判断是否有写权限
boolean hasWrite = (userPerm & WRITE) != 0;
System.out.println("是否有写权限:" + hasWrite); // true
// 增加删权限
userPerm |= DELETE; // 0111
// 取消读权限
userPerm &= ~READ; // 0110
System.out.println("更新后权限(十进制):" + userPerm); // 6(0110)
// 2. 左移/右移的高效计算(替代乘除2的n次方)
int num = 10;
System.out.println("左移2位(×4):" + (num << 2)); // 40
System.out.println("算术右移2位(÷4):" + (num >> 2)); // 2
// 3. 位异或的特性:a^a=0,a^0=a(交换两个数)
int x = 5, y = 3;
x = x ^ y; // x=6(110)
y = x ^ y; // y=5(101)
x = x ^ y; // x=3(011)
System.out.println("交换后x=" + x + ", y=" + y); // 3,5
}
}
3. 避坑指南
- 移位别超范围:
int
左移超过 31 位、long
超过 63 位会取模(如a<<33
等效于a<<1
); - 负数移位注意符号:算术右移(>>)会补符号位,无符号右移(>>>)会补 0,负数用 >>> 可能得到大正数;
- 位运算仅适用于整数:浮点数不能用位运算符,编译会报错。
六、运算符优先级:避免 "算错值" 的核心规则
运算符优先级决定了表达式的计算顺序,优先级高的先执行;若优先级相同,则按 "结合性"(左结合 / 右结合)计算。记不住优先级?加括号就对了!
1. 优先级总表(从高到低,重点标红)
优先级 | 运算符组 | 运算符 | 结合性 | ||
---|---|---|---|---|---|
1 | 括号 | ()、[]、. | 左结合 | ||
2 | 单目运算符 | ++、--、~、!、(类型) | 右结合 | ||
3 | 算术运算符(乘除) | *、/、% | 左结合 | ||
4 | 算术运算符(加减) | +、- | 左结合 | ||
5 | 移位运算符 | <<、>>、>>> | 左结合 | ||
6 | 比较运算符 | >、<、>=、<= | 左结合 | ||
7 | 相等运算符 | ==、!= | 左结合 | ||
8 | 位与 | & | 左结合 | ||
9 | 位异或 | & | 左结合 | ||
10 | 位或 | 左结合 | |||
11 | 逻辑与 | && | 左结合 | ||
12 | 逻辑或 | 左结合 | |||
13 | 三目运算符 | 条件?表达式 1: 表达式 2 | 右结合 | ||
14 | 赋值运算符 | =、+=、-=、*=、/=、%= 等 | 右结合 |
2. 易错优先级示例解析
csharp
public class PriorityDemo {
public static void main(String[] args) {
// 示例1:算术 vs 比较 vs 逻辑
boolean res1 = 10 + 5 > 12 && 3 < 5;
// 执行顺序:(10+5) >12 → 15>12=true;true && (3<5) → true
System.out.println("res1=" + res1); // true
// 示例2:自增 vs 加法(单目优先级高于算术)
int a = 2;
int res2 = a++ + ++a;
// 执行顺序:a++(先用a=2,后a=3);++a(先a=4,后用4);2+4=6
System.out.println("res2=" + res2); // 6
// 示例3:赋值 vs 三目(三目优先级高于赋值)
int b = 3, c = 5;
int res3 = b > c ? b : c;
// 执行顺序:(b>c)→false;取c=5;赋值给res3
System.out.println("res3=" + res3); // 5
// 反例:不加括号的坑
int res4 = 10 > 5 ? 2 + 3 : 4 + 5;
// 正确:(10>5)→true;(2+3)=5 → res4=5
int res5 = 10 > 5 ? 2 + 3 : 4 * 5;
// 正确:(4*5)优先级高于三目?不!三目优先级13>赋值14,但算术3>三目13
// 执行顺序:(10>5)→true;(2+3)=5;res5=5(若加括号更清晰)
}
}
七、总结:从 "会用" 到 "用对" 的 3 个核心原则
- 优先加括号:即使记准优先级,复杂表达式加括号也能提升可读性,避免团队协作中的误解;
- 关注 "坑点细节" :整数除法转浮点数、浮点数不直接 ==、引用类型用 equals、逻辑短路影响执行顺序,这些是面试和开发中的高频错题;
- 位运算用在刀刃上:位运算效率高,但可读性差,仅在权限控制、性能敏感场景使用,普通计算优先用算术运算符。
运算符是 Java 的基础,但基础不牢则上层建筑不稳。掌握本文的用法、坑点和优先级规则,能帮你避免 80% 的运算符相关 bug,让代码更高效、更健壮。