你必须知道的三目运算符的BUG

前言

文本已收录至我的GitHub仓库,欢迎Star:https://github.com/bin392328206/six-finger
种一棵树最好的时间是十年前,其次是现在
我知道很多人不玩qq了,但是怀旧一下,欢迎加入六脉神剑Java菜鸟学习群,群聊号码:549684836 鼓励大家在技术的路上写博客

每篇一句

这世上每个人都背负着枷锁,有的人是别人给的,有的是自己给的。

絮叨

三目运算符一直是众多开发者信手拈来的一种写法,它简化了if-else的臃肿的写法,而是用一行代码替代,就感觉无形之中秀了一把。殊不知,这么帅气的代码也暗藏着一个BUG。

缘由

头天晚上发布了一个功能,本以为是波澜不惊的一个需求,结果第二天kibana打出了成吨的NPE日志。这些NPE日志大多都不约而同都指向了我写的一行代码,我立马推了下我的眼镜,开始排查起来了。

问题代码

Kibana的堆栈日志定位在第899行。

resultMap.put("unAuditPurchaseOrder", switchConf == null ? 0 : switchConf.getUnAuditPurchaseOrder());

1.检查了resultMap,它在上面有实例化,不可能为空。2.检查switchConf,但是在这里有判空,也不会报错。那是怎么回事????

继续排查

既然肉眼看不出,那么只能找一台测试机,用一下Arthas看一下具体的情况。(线上慎用,因为可能会造成卡顿)

trace com.raycloud.dmj.tj.services.customer.CustomerButtonService getPurchaseConfig -n 5 '1==1' --skipJDKMethod false

果然,这里就发现了端倪。这里竟然执行了intValue()!也就是说如果switchConf.getUnAuditPurchaseOrder()这个是null,那么就很明显发生了NPE。

看一下字节码

为了显现效果,我换一个简单的程序

public class Test {

    public static void main(String[] args) {

        Integer i = null;
        System.out.println(1 != 1 ? 0 : i);

    }
}

定位到class文件目录,执行

javap -c -l Test

然后我又改了一下程序

public class Test {

    public static void main(String[] args) {

        Integer i = null;
        System.out.println(1 == 1 ? 0 : i);

    }
}

这次运行并不会报错,看一下它的JVM指令:

因为三目运算符的结果是前者的逻辑,即返回一个常量0。

解释

由上面的实验可以发现,JVM在解释三目运算符的时候,会对两个逻辑语句进行数据类型校验,按照前者的数据类型为准。实验中,数据类型是基本数据类型,所以,如果逻辑走到了后者,那么就会进行自动的拆箱。这个隐式的操作就是造成这个BUG的原因。

解决

既然知道原因了,那么只要统一数据类型就行:

public class Test {

    public static void main(String[] args) {

        Integer i = null;
        System.out.println(1 != 1 ? new Integer(0) : i);

    }
}

然后按照惯例,我们还是看一下他的JVM指令:

拾遗

后来在无意之中发现,原来这个例子在《阿里巴巴开发手册》当中也有被记录

其实说的也是一回事情!

结尾

文章出自掘金 浮沉_fuchen

日常求赞

好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是真粉

创作不易,各位的支持和认可,就是我创作的最大动力,我们下篇文章见

六脉神剑 | 文 【原创】如果本篇博客有任何错误,请批评指教,不胜感激 !

<think>嗯,用户这次专门询问Python中"|"运算符的用法,看来是在编程中遇到了具体需求。结合用户之前提供的参考资料,ta似乎正在系统学习Python运算符体系,之前已了解过成员运算符、比较运算符三目运算符。用户可能正在处理涉及位运算或集合操作的实际问题,也可能是阅读代码时遇到了这个符号。从用户提问方式看,ta特别强调要"用法、解释、示例",说明需要实操性指导。考虑到用户引用过运算符的性能分析内容,可能对底层实现也有兴趣。有趣的是,用户刻意用空格包裹竖线写成"|",显然是为了避免被误认为Markdown表格符号,这种细节意识值得肯定。关于"|"运算符,需要分场景说明:在数值计算中它是按位或,对于集合则是并集。这个符号在不同场景下的重载正是Python灵活性的体现。用户引用的资料[1]提到成员运算符优化策略,这个思路同样适用于位运算——用位掩码替代列表可大幅提升性能,这点值得在回答中呼应。特别要注意的是,用户提供的参考引用[2]提到逻辑运算符,但"|"和逻辑或"or"有本质区别。前者是位操作/集合操作符,后者用于布尔短路求值。这个关键区别必须强调,否则容易引发隐蔽bug。资料[3]的三目运算符内容虽不直接相关,但暗示用户可能接触过条件表达式,可在回答中类比说明"|"在条件筛选中的特殊用途。针对匿名用户的学习特点,示例应当直白有效。第一个示例用二进制视角演示位或运算最直观,而集合示例则选用差异明显的两个集合演示并集。考虑到用户可能是初学者,特别添加了位掩码的实际应用案例——权限控制是日常开发中最易懂的场景。</think> 在 Python 中,`|` 运算符有两种主要含义,具体取决于操作数的类型: **一、按位或运算符(用于整数)** * **含义**:对整数的二进制位执行 **逻辑或** 操作。只有对应位中至少有一个是 1 时,结果的对应位才是 1。 * **语法**:`x | y` * **示例**: ```python # 二进制: 5 -> 0101, 3 -> 0011 # 按位或: 0101 | 0011 = 0111 (十进制 7) result = 5 | 3 print(result) # 输出: 7 ``` **二、集合并集运算符(用于集合/集合类型)** * **含义**:计算两个集合的 **并集**,返回一个包含两个集合中所有 **唯一元素** 的新集合。 * **语法**:`set_a | set_b` * 也可用于 `frozenset` 或 `collections.Counter`(但 `Counter` 的 `|` 会取对应计数的最大值)。 * **示例**: ```python set1 = {1, 2, 3} set2 = {3, 4, 5} union_set = set1 | set2 # 等价于 set1.union(set2) print(union_set) # 输出: {1, 2, 3, 4, 5} ``` **关键区别** | 特性 | 按位或 (`|`) | 集合并集 (`|`) | | :--------- | :------------------- | :----------------------- | | **操作数** | 整数 (`int`) | 集合 (`set`, `frozenset`)| | **功能** | 操作二进制位 | 组合两个集合的元素 | | **结果类型**| 整数 (`int`) | 新集合 (`set`) | | **等价方法**| `x.__or__(y)` | `set_a.union(set_b)` | **注意事项** 1. **类型兼容性**:Python 会根据操作数的类型自动选择执行按位或还是集合并集。如果操作数类型不支持 `|` 操作(如两个列表),会引发 `TypeError`。 2. **优先级**:`|` 运算符的优先级比比较运算符(如 `<`, `>`, `==`)高,但比算术运算符(如 `+`, `-`, `*`, `**`)和位运算符 `&` 低。必要时使用括号 `()` 明确运算顺序。 3. **短路求值**:**不适用**。`|` 不是逻辑运算符,不会短路求值。 4. **字典视图**:字典的键视图 (`dict_keys`)、值视图 (`dict_values`) 或项视图 (`dict_items`) **不直接**支持 `|` 运算符。需要先转换为集合:`set(dict1.keys()) | set(dict2.keys())`。 5. **自定义对象**:可以通过定义 `__or__` 或 `__ior__` 方法来自定义类的对象对 `|` 和 `|=` 的行为。 **示例:混合应用** ```python # 位运算应用:组合状态标志 READ = 0b0001 # 1 WRITE = 0b0010 # 2 EXECUTE = 0b0100 # 4 user_permissions = READ | WRITE # 0b0011 (3) can_execute = user_permissions & EXECUTE # 0b0000 (0) -> 没有执行权限 # 集合运算应用:合并标签 tags_article1 = {'python', 'tutorial'} tags_article2 = {'python', 'data', 'science'} all_tags = tags_article1 | tags_article2 # {'python', 'tutorial', 'data', 'science'} ``` **总结** Python 中的 `|` 是一个 **多功能运算符**,其核心作用取决于操作数的类型: 1. **按位或**:操作整数,合并二进制位 (1 的状态)。 2. **集合并集**:操作集合,合并所有唯一元素。 了解操作数的数据类型是准确理解和使用 `|` 运算符的关键[^1][^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值