运算符与表达式
学习目标
- 会使用算术、比较、逻辑、赋值、自增和条件运算符表达常见计算。
- 理解
&&、||的短路规则,区分逻辑运算和位运算的用途。 - 能识别常见优先级陷阱,使用括号把表达式意图写清楚。
核心知识点
专业术语
- 运算符(operator):对一个或多个操作数执行计算、比较或组合的符号。
- 表达式(expression):求值后产生一个结果的代码片段;表达式也可能产生副作用,如
count++。 - 短路求值(short-circuit evaluation):
&&左侧为假或||左侧为真时不再计算右侧。 - 位运算(bitwise operation):直接操作整数二进制位的
&、|、^、~和移位运算。 - 条件运算符(conditional operator):
condition ? whenTrue : whenFalse,根据条件在两个表达式中选一个。 - 优先级(precedence)/结合性(associativity):决定没有括号时的分组和求值顺序,但不应代替清晰括号。
白话解释与边界
算术运算
算术运算包括 +、-、*、/、%;整数除法会截去小数,除以零会抛出异常,+ 在有字符串操作数时还会变成拼接。
比较运算
比较运算产生 boolean,基本类型比较数值;引用类型的 == 比较是否同一对象,对象内容通常使用 equals。
逻辑运算与短路
逻辑运算 !、&&、|| 处理布尔值。&& 和 || 从左向右短路,常用来先判空再访问对象;单个 & 和 | 对布尔值也能计算,但会始终计算两侧,不能拿来替代短路保护。
位运算与移位
位运算 &、|、^、~ 和移位 <<、>>、>>> 面向整数的二进制位模式;权限掩码是常见用途,普通业务计算不应为追求短小而改用位操作。
复合赋值与自增
复合赋值如 +=、*= 会先计算再写回,并包含隐式窄化规则,例如 short n = 1; n += 2; 可以编译,而 n = n + 2; 的结果是 int,需要显式转换。++/-- 的前置版本先改变值再参与表达式,后置版本先使用旧值再改变;复杂表达式中应拆成多条语句。
三元运算
三元运算符适合简单的二选一并产生一个值,分支较长时使用 if。优先级容易混淆的地方应主动加括号:乘除通常先于加减,比较先于 &&,&& 先于 ||,赋值优先级较低;完整表不如明确分组可靠。
优先级与括号
括号优先表达意图,尤其是在比较、逻辑和赋值混合的表达式中;即使语言优先级已经确定,括号也能降低维护者的理解成本。
| 类别 | 常见运算符 | 典型边界 |
|---|---|---|
| 算术 | + - * / % | 整数除法截断,整数结果仍可能溢出 |
| 比较 | == != < <= > >= | 基本类型比值,引用类型 == 比身份 |
| 逻辑 | `! && | |
| 位 | `& | ^ ~ << >> >>>` |
| 赋值 | = += -= *= /= %= ... | 复合赋值可能隐含转换,先明确目标类型 |
| 条件 | condition ? a : b | 适合简单二选一,不宜嵌套复杂条件 |
常用用法
&& 与 ||:使用短路逻辑
短路逻辑会在结果已确定时跳过右侧表达式,常用于先判空再调用方法;有副作用的表达式不要依赖这种跳过行为。
// 语义:短路逻辑会在结果已确定时跳过右侧表达式,常用于先判空再调用方法。
public class ShortCircuitDemo {
static boolean hasText(String text) {
return text != null && !text.isBlank();
}
public static void main(String[] args) {
System.out.println(hasText(null));
// 输出:false
}
}三元运算:在表达式中选择一个值
三元运算适合简单二选一;分支包含多条语句或副作用时改用 if 更清楚。
// 语义:三元运算适合简单二选一。
// 初始状态:score 初始为 80;result 初始为 score >= 60 ? "pass" : "retry"。
public class TernaryDemo {
public static void main(String[] args) {
int score = 80;
// 关键变化:score 接收表达式 80 的计算结果。
// 初始状态:score 的初始值为 80。
String result = score >= 60 ? "pass" : "retry";
System.out.println(result);
// 输出:pass
}
}不常用但需要知道
复合赋值:先计算再写回
复合赋值包含隐式窄化;count = count + 2 的结果是 int,不能把两种写法的类型规则混为一谈。
public class CompoundAssignmentDemo {
public static void main(String[] args) {
short count = 1;
// 关键变化:count 接收表达式 1 的计算结果。
// 初始状态:count 的初始值为 1。
count += 2;
System.out.println(count);
// 输出:3
}
}位移运算:处理整数位模式
位运算适合标志位和底层编码;普通条件判断优先使用 &&、||,避免把位运算当成布尔短路。
public class BitOperationDemo {
public static void main(String[] args) {
int flags = 1 << 3;
// 关键变化:flags 接收表达式 1 << 3 的计算结果。
// 初始状态:flags 的初始值为 1 << 3。
System.out.println(flags);
// 输出:8
}
}简单案例
public class OperatorDemo {
static boolean hasText(String text) {
return text != null && !text.isBlank(); // 右侧在 null 时不会执行
}
public static void main(String[] args) {
int a = 7;
int b = 3;
System.out.println("arithmetic=" + (a / b) + "," + (a % b));
// 输出:arithmetic=2,1
int permissions = 0b0101;
int write = 0b0010;
int updated = permissions | write;
boolean canWrite = (updated & write) != 0;
System.out.println("permissions=" + updated + ", canWrite=" + canWrite);
// 输出:permissions=7, canWrite=true
String value = "Java";
System.out.println("hasText=" + hasText(value) + ", missing=" + hasText(null));
// 输出:hasText=true, missing=false
int count = 4;
int before = count++;
int after = ++count;
System.out.println("before=" + before + ", after=" + after + ", count=" + count);
// 输出:before=4, after=6, count=6
String label = count > 5 ? "many" : "few";
System.out.println(label);
// 输出:many
int result = (a + b) * 2; // 用括号明确先做加法
System.out.println("result=" + result);
// 输出:result=20
}
}案例中的 hasText 展示了短路保护:传入 null 时 !text.isBlank() 不会被执行。权限位使用 | 增加标志、& 检查标志;前后置自增则通过拆开的变量展示顺序,避免把多个副作用塞入一条表达式。
易混点
&&/||会短路,&/|不短路;即使对布尔值,二者也不能随意互换。- 基本类型的
==比较值,引用类型的==比较身份;对象内容通常使用equals。 i++先返回旧值,++i先改变再返回新值;复杂表达式应避免同时出现多个自增。=是赋值,==是比较;+=等复合赋值还可能包含隐式转换。- 三元表达式只能选两个结果;条件多、分支有副作用或需要多条语句时应改用
if。 - 负数右移的
>>与>>>结果不同,位运算不应代替一般金额或业务计算。
课后小问
user != null && user.isActive()为什么比user.isActive() && user != null安全? 答案:前者先判断user不为空,若为空就因短路跳过方法调用;后者会先调用空引用的方法而抛出异常。 解析:短路运算的操作数按从左到右求值,保护条件必须放在可能失败的访问之前;这也是“先校验、后使用”的常见写法。int x = 2 + 3 * 4的结果是多少?怎样让它先加后乘? 答案:结果是 14,因为乘法优先于加法;写成int x = (2 + 3) * 4后结果是 20。 解析:不要依赖别人记住完整优先级表,表达意图时使用括号;括号不会改变已经明确的逻辑,只会减少阅读和维护成本。为什么
permissions & write能判断某个权限位是否存在? 答案:按位与只保留两个数都为 1 的位,结果不为 0 就表示write的标志位在permissions中被设置。 解析:位掩码适合紧凑表示多个独立开关;先定义每个标志只占一个位,并用括号明确比较范围,避免和!=的优先级混淆。
本节小结
- 算术、比较和逻辑运算表达值关系,整数除法、溢出和字符串拼接都有明确边界。
&&与||会短路,位运算则处理整数位模式,不能把二者当作同义写法。- 复合赋值和自增会产生隐式转换或副作用,复杂场景应拆成易读的语句。
- 三元运算符表达简单二选一,优先级不确定时用括号明确求值分组。
快速回顾
- 能区分算术、比较、逻辑、位、赋值和三元运算符的用途。
- 能判断短路是否会执行右侧表达式,并用它保护空引用访问。
- 能解释前置/后置自增的差异和复合赋值的转换边界。
- 能用括号消除混合表达式中的优先级歧义。
阿源的知识库