Skip to content

运算符与表达式 ​

学习目标 ​

  • 会使用算术、比较、逻辑、赋值、自增和条件运算符表达常见计算。
  • 理解 &&、|| 的短路规则,区分逻辑运算和位运算的用途。
  • 能识别常见优先级陷阱,使用括号把表达式意图写清楚。

核心知识点 ​

专业术语 ​

  • 运算符(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适合简单二选一,不宜嵌套复杂条件

常用用法 ​

&& 与 ||:使用短路逻辑 ​

短路逻辑会在结果已确定时跳过右侧表达式,常用于先判空再调用方法;有副作用的表达式不要依赖这种跳过行为。

java
// 语义:短路逻辑会在结果已确定时跳过右侧表达式,常用于先判空再调用方法。
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 更清楚。

java
// 语义:三元运算适合简单二选一。
// 初始状态: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,不能把两种写法的类型规则混为一谈。

java
public class CompoundAssignmentDemo {
    public static void main(String[] args) {
        short count = 1;
// 关键变化:count 接收表达式 1 的计算结果。
// 初始状态:count 的初始值为 1。
        count += 2;
        System.out.println(count);
        // 输出:3
    }
}

位移运算:处理整数位模式 ​

位运算适合标志位和底层编码;普通条件判断优先使用 &&、||,避免把位运算当成布尔短路。

java
public class BitOperationDemo {
    public static void main(String[] args) {
        int flags = 1 << 3;
// 关键变化:flags 接收表达式 1 << 3 的计算结果。
// 初始状态:flags 的初始值为 1 << 3。
        System.out.println(flags);
        // 输出:8
    }
}

简单案例 ​

java
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。
  • 负数右移的 >> 与 >>> 结果不同,位运算不应代替一般金额或业务计算。

课后小问 ​

  1. user != null && user.isActive() 为什么比 user.isActive() && user != null 安全? 答案:前者先判断 user 不为空,若为空就因短路跳过方法调用;后者会先调用空引用的方法而抛出异常。 解析:短路运算的操作数按从左到右求值,保护条件必须放在可能失败的访问之前;这也是“先校验、后使用”的常见写法。

  2. int x = 2 + 3 * 4 的结果是多少?怎样让它先加后乘? 答案:结果是 14,因为乘法优先于加法;写成 int x = (2 + 3) * 4 后结果是 20。 解析:不要依赖别人记住完整优先级表,表达意图时使用括号;括号不会改变已经明确的逻辑,只会减少阅读和维护成本。

  3. 为什么 permissions & write 能判断某个权限位是否存在? 答案:按位与只保留两个数都为 1 的位,结果不为 0 就表示 write 的标志位在 permissions 中被设置。 解析:位掩码适合紧凑表示多个独立开关;先定义每个标志只占一个位,并用括号明确比较范围,避免和 != 的优先级混淆。

本节小结 ​

  • 算术、比较和逻辑运算表达值关系,整数除法、溢出和字符串拼接都有明确边界。
  • && 与 || 会短路,位运算则处理整数位模式,不能把二者当作同义写法。
  • 复合赋值和自增会产生隐式转换或副作用,复杂场景应拆成易读的语句。
  • 三元运算符表达简单二选一,优先级不确定时用括号明确求值分组。

快速回顾 ​

  • 能区分算术、比较、逻辑、位、赋值和三元运算符的用途。
  • 能判断短路是否会执行右侧表达式,并用它保护空引用访问。
  • 能解释前置/后置自增的差异和复合赋值的转换边界。
  • 能用括号消除混合表达式中的优先级歧义。

继续阅读 ​