短路与(&&)和短路或(||)与普通逻辑运算符(&、|)最根本区别在于执行路径:&&在左操作数为false时跳过右表达式,||在左操作数为true时跳过右表达式;而&和|无论左值如何均强制执行两侧,影响副作用发生与否及类型约束。

短路与(&&)和短路或(||)跟普通逻辑运算符(&、|)最根本的区别,不在结果上,而在执行路径是否“走到底”。
执行时机:要不要算右边,取决于左边的值
普通逻辑运算符不管左边真假,一定执行右边表达式:
-
&:即使左边是
false,仍会计算右边——比如obj == null & obj.toString(),obj 为 null 时仍调用 toString(),直接抛异常; -
|:即使左边是
true,右边照样执行——比如isValid() | saveToDB(),校验通过后仍会强行写库,哪怕没必要。
短路运算符则按逻辑规则提前终止:
-
&&:左边为
false,整个结果必为false,右边跳过不执行; -
||:左边为
true,整个结果必为true,右边完全不触发。
副作用是否发生,是关键分水岭
所谓“副作用”,就是表达式执行时带来的额外影响,比如变量自增、方法调用、IO 操作、状态修改等。普通运算符强制触发所有副作用;短路运算符只在必要时才触发。
- 写法:
flag & i++ > 0→i每次都加 1; - 写法:
flag && i++ > 0→flag为false时,i值不变。
这种差异在空指针防护、资源控制、日志埋点、权限校验等场景中不是“性能优化”,而是“能否正确运行”的问题。
类型约束与语义定位不同
& 和 | 是多面手:既能做布尔逻辑运算,也能做整数位运算(如 5 & 3 得 1),编译器靠操作数类型自动切换语义;
&& 和 || 是专精者:只接受布尔表达式,语法上禁止用于数值——5 && 3 直接编译失败。
这意味着,当你看到 && 或 ||,它代表的是“条件控制流”的意图;而 & 或 | 出现在布尔上下文中,往往暗示你可能误用了位运算符,或刻意需要两边都执行。
底层机制其实是“条件跳转”,不是电路模拟
有些解释用串联/并联电路类比,容易引发误解。实际上 JVM 或 C 编译器生成的指令是标准的条件跳转:
-
a && b编译后类似:判断 a;若假,跳到结束;否则继续算 b; -
a & b编译后就是:算 a → 算 b → 按位/逻辑与,没有跳转分支。
这个跳转动作极轻量,但正是它让短路行为成为可能——不是“省电”,而是“按需执行”。











