3. Java 运算符
一、算术运算符
1.1 基本运算符
| 运算符 | 名称 | 示例 | 说明 |
|---|---|---|---|
+ |
加法 | a + b |
也可用于字符串拼接 |
- |
减法 | a - b |
|
* |
乘法 | a * b |
|
/ |
除法 | a / b |
整数除法截断小数,非四舍五入 |
% |
取余 | a % b |
返回除法余数 |
1.2 整数除法
整数除法直接丢弃小数部分,不进行四舍五入:
int a = 7 / 2; // 结果:3
int b = 5 / 10; // 结果:0
若需保留小数,至少一个操作数为浮点类型:
double c = 7.0 / 2; // 3.5
double d = (double) 7 / 2; // 3.5
常见错误:double result = 7 / 2; 结果为 3.0,因整数除法优先于隐式转换。
1.3 取余运算
int r1 = 10 % 3; // 1
int r2 = 15 % 5; // 0
int r3 = -7 % 3; // -1(符号与被除数一致)
典型应用:判断奇偶 — num % 2 == 0。
1.4 自增自减(++ / --)
| 形式 | 名称 | 行为 |
|---|---|---|
++a |
前缀自增 | 先加 1,再使用新值 |
a++ |
后缀自增 | 先使用原值,再加 1 |
--a |
前缀自减 | 先减 1,再使用新值 |
a-- |
后缀自减 | 先使用原值,再减 1 |
int a = 5;
int b = ++a; // a = 6, b = 6
int c = 5;
int d = c++; // d = 5, c = 6
表达式中的执行顺序:
int a = 1;
int b = a++ + ++a;
// a++:返回 1,a 变为 2
// ++a:a 变为 3,返回 3
// b = 1 + 3 = 4
// 最终:a = 3, b = 4
规范建议:同一表达式中不对同一变量多次使用自增/自减,建议拆分。
二、字符串拼接
2.1 拼接规则
+ 运算符的语义取决于操作数类型:当任一操作数为 String 时,执行字符串拼接;否则执行数值加法。
System.out.println(1 + 2 + "3"); // "33" → (1+2)=3, 再拼接 "3"
System.out.println("1" + 2 + 3); // "123" → 从左到右依次拼接
若非字符串操作数的计算需优先执行,使用括号:
System.out.println("和:" + (10 + 20)); // "和:30"
2.2 char 类型与拼接
char ch = 'A'; // ASCII 65
System.out.println(ch + 1); // 66(算术运算)
System.out.println("字母:" + ch + 1); // "字母:A1"
System.out.println("字母:" + (ch + 1)); // "字母:66"
System.out.println("字母:" + (char)(ch + 1)); // "字母:B"
2.3 性能说明
循环中频繁使用 + 拼接会创建大量临时 String 对象,推荐使用 StringBuilder:
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append(i);
}
String result = sb.toString();
三、赋值运算符
3.1 简单赋值与链式赋值
= 为右结合运算符,从右向左执行:
int a, b, c;
a = b = c = 10;
// 等价于 a = (b = (c = 10));
// 执行顺序:c=10 → b=10 → a=10
3.2 复合赋值
| 运算符 | 示例 | 等价形式 |
|---|---|---|
+= |
a += 3 |
a = a + 3 |
-= |
a -= 2 |
a = a - 2 |
*= |
a *= 4 |
a = a * 4 |
/= |
a /= 2 |
a = a / 2 |
%= |
a %= 3 |
a = a % 3 |
&= |
a &= b |
a = a & b |
| ` | =` | `a |
^= |
a ^= b |
a = a ^ b |
<<= |
a <<= 2 |
a = a << 2 |
>>= |
a >>= 1 |
a = a >> 1 |
>>>= |
a >>>= 1 |
a = a >>> 1 |
重要特性 — 自动类型强转:
Java 语言规范规定:E1 op= E2 等价于 E1 = (T)((E1) op (E2)),其中 T 为 E1 的类型。复合赋值自动执行窄化转换:
short s = 10;
s = s + 5; // 编译错误:s + 5 结果为 int
s += 5; // 正确:等价于 s = (short)(s + 5)
byte b = 10;
b += 100; // 正确:自动强转
b = b + 100; // 编译错误
3.3 常见问题
- 浮点字面量默认为
double:float f = 3.14;编译错误,正确写法float f = 3.14f; - 不支持对常量/表达式赋值:
5 = a;和(a + b) = 10;均为非法
四、关系运算符
4.1 运算符列表
| 运算符 | 名称 | 示例 | 返回值 |
|---|---|---|---|
== |
等于 | a == b |
boolean |
!= |
不等于 | a != b |
boolean |
< |
小于 | a < b |
boolean |
<= |
小于等于 | a <= b |
boolean |
> |
大于 | a > b |
boolean |
>= |
大于等于 | a >= b |
boolean |
关系运算符适用于所有基本数值类型和引用类型。字符间也可比较(基于 Unicode 值)。
4.2 == 与 equals()
- 基本类型:
==比较值 - 引用类型:
==比较内存地址,equals()比较内容(需类正确重写)
String s1 = "Hello"; // 字符串常量池
String s2 = new String("Hello"); // 堆中新对象
System.out.println(s1 == s2); // false(地址不同)
System.out.println(s1.equals(s2)); // true(内容相同)
字符串常量池特性:
String s3 = "abc";
String s4 = "abc";
System.out.println(s3 == s4); // true(同一常量池对象)
规范建议:字符串内容比较统一使用 equals()。
4.3 浮点数比较
二进制无法精确表示十进制小数 0.1、0.2、0.3(在二进制中均为无限循环小数),导致精度误差:
double a = 0.1 + 0.2;
System.out.println(a); // 0.30000000000000004
System.out.println(a == 0.3); // false
安全比较方案:
// 方案一:误差范围(适用一般计算)
public static boolean isEqual(double a, double b) {
return Math.abs(a - b) < 1e-9;
}
// 方案二:BigDecimal(适用精确计算,如金融场景)
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.2");
BigDecimal sum = a.add(b);
System.out.println(sum.equals(new BigDecimal("0.3"))); // true
注意:BigDecimal 构造必须使用字符串参数,new BigDecimal(0.1) 已携带精度误差。
4.4 关系运算符限制
| 操作数组合 | 是否允许 | 说明 |
|---|---|---|
| boolean 与 boolean | 允许 | 仅 == 和 != |
| boolean 与数值 | 不允许 | 编译错误 |
| 对象与对象 | 允许 | 仅 == 和 !=,比较引用地址 |
五、逻辑运算符
5.1 基本逻辑运算符
| 运算符 | 名称 | 真值表 | 短路 |
|---|---|---|---|
& |
逻辑与 | 全 true 则 true | 否 |
| ` | ` | 逻辑或 | 有 true 则 true |
^ |
逻辑异或 | 不同为 true,相同为 false | 否 |
! |
逻辑非 | 取反 | — |
&& |
短路与 | 同 & |
是 |
| ` | ` | 短路或 |
5.2 短路与非短路的区别
短路运算符在左侧结果可确定整体结果时,跳过右侧表达式的执行:
String str = null;
if (str != null && str.length() > 0) { // 安全:str 为 null 时右侧不执行
// ...
}
// if (str != null & str.length() > 0) // 危险:右侧必执行,抛出 NullPointerException
int a = 1;
boolean r1 = (a > 2) && (a++ > 0); // r1 = false, a = 1(短路,a++ 未执行)
boolean r2 = (a > 2) & (a++ > 0); // r2 = false, a = 2(非短路,a++ 已执行)
5.3 异或应用
// 布尔值翻转
boolean flag = true;
flag = flag ^ true; // false
flag = flag ^ true; // true
// 整数交换(不推荐用于生产代码)
int a = 5, b = 10;
a = a ^ b;
b = a ^ b;
a = a ^ b;
// 结果:a = 10, b = 5
5.4 使用建议
- 条件判断优先使用
&&和||(短路,更安全) &仅用于:需要两侧均执行(如右侧有副作用)或位运算场景^不可用于幂运算,Java 中幂运算使用Math.pow()
六、三元运算符
6.1 语法
condition ? expr1 : expr2
条件为 true 返回 expr1,否则返回 expr2。expr1 与 expr2 类型需兼容。
int max = a > b ? a : b;
6.2 优先级
三元运算符优先级仅高于赋值运算符,低于逻辑运算符:
int result = true || false ? 10 : 20;
// 等价于 (true || false) ? 10 : 20,结果为 10
6.3 使用原则
- 适用于简单的二选一赋值
- 避免嵌套,多层嵌套应改用
if-else - 建议用括号明确运算顺序
七、运算符优先级
7.1 优先级表(降序)
| 优先级 | 运算符类别 | 运算符 | 结合性 |
|---|---|---|---|
| 1 | 括号 | () |
左 |
| 2 | 单目 | ! - ++ -- |
右 |
| 3 | 算术 | * / % |
左 |
| 4 | 算术 | + - |
左 |
| 5 | 位移 | << >> >>> |
左 |
| 6 | 关系 | < <= > >= instanceof |
左 |
| 7 | 相等 | == != |
左 |
| 8 | 位与 | & |
左 |
| 9 | 位异或 | ^ |
左 |
| 10 | 位或 | ` | ` |
| 11 | 逻辑与 | && |
左 |
| 12 | 逻辑或 | ` | |
| 13 | 三元 | ?: |
右 |
| 14 | 赋值 | = += -= 等 |
右 |
7.2 关键规则
- 括号优先级最高,不确定时使用括号明确意图
- 大方向:算术 > 关系 > 逻辑 > 三元 > 赋值
&&优先级高于||
// 示例
boolean r1 = 3 + 5 > 7 && 9 - 2 < 8; // (8>7) && (7<8) → true
boolean r2 = true || false && true; // true || (false && true) → true
int res = (1 + 2) * 3; // 9
八、类型转换
8.1 隐式转换(自动类型提升)
从小范围到大范围自动转换,遵循提升链:
byte → short → int → long → float → double
表达式中的类型提升:
byte a = 1, b = 2;
// byte sum = a + b; // 编译错误:结果提升为 int
int sum = a + b; // 正确
char 可参与整数提升:
char c = 'A'; // ASCII 65
int n = c; // 65
8.2 强制转换(显式类型转换)
大范围到小范围使用 (目标类型) 语法,可能导致精度丢失或溢出:
double d = 123.89;
int i = (int) d; // 123(截断,非四舍五入)
int x = 300;
byte b = (byte) x; // 44(溢出:300 - 256 = 44)
8.3 转换限制
boolean与任何数值类型之间不可互转char与int/long等整数类型可互转(char 为 16 位无符号整数)
8.4 编译期常量优化
byte b = 100; // 正确:100 在 byte 范围内,编译期确定
int i = 100;
// byte b2 = i; // 错误:i 为变量,编译器无法确定范围
byte b3 = (byte) i; // 正确:显式强转
九、常见问题
Q1:short s = 1; s = s + 1; 与 s += 1; 的区别?
s = s + 1 编译错误,因 s + 1 结果为 int,不可直接赋值给 short。s += 1 正确,复合赋值自动执行 (short)(s + 1) 强转。
Q2:"abc" == "abc" 与 new String("abc") == "abc"?
前者 true(同一常量池对象),后者 false(堆中新建对象)。字符串内容比较应统一使用 equals()。
Q3:0.1 + 0.2 == 0.3 为何为 false?
十进制小数 0.1、0.2、0.3 在二进制中为无限循环小数,double 只能存储近似值。0.1+0.2 的近似结果约等于 0.30000000000000004。
Q4:& 与 && 的核心区别?
& 两侧均执行(非短路),&& 左侧为 false 时跳过右侧(短路)。条件判断应使用 &&,位运算使用 &。
Q5:int a = 5; int b = (a = 3) + 2; 的结果?
a = 3, b = 5。(a = 3) 先执行赋值并返回值 3,因此 b = 3 + 2 = 5。体现赋值运算符的返回值特性。
更多推荐
所有评论(0)