尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Java 运算符:i = i++ 到底等于几?一文彻底搞懂 12 类运算符的底层逻辑

Java 运算符:i = i++ 到底等于几?一文彻底搞懂 12 类运算符的底层逻辑 从短路运算的 NPE 陷阱到位运算的权限校验从字节码到优先级这篇带你全面通关我们之前聊过数据类型定义“值长什么样”int、String、char变量类型定义“值存在哪、活多久”局部、实例、静态修饰符定义“谁能用、有什么特殊功能”public、static、volatile今天我们来补上 Java 语法体系的最后一块核心拼图运算符Operators。如果说变量是“数据容器”那么运算符就是“数据加工厂”。理解了运算符你就理解了 Java 程序如何“动”起来。一、Java 运算符全景分类12 大类分类运算符优先级大致算术运算符-*/%--高关系/比较运算符!中逻辑运算符位运算符^ ~ 赋值运算符-*/%^ 条件运算符三元? :低instanceofinstanceof中其他括号()、数组下标[]、成员访问.最高核心结论括号()优先级最高赋值优先级最低。掌握这个就能避免 90% 的优先级混乱。二、9 大高频陷阱与硬核解析面试必看陷阱 1i i到底等于几经典送命题inti0;ii;System.out.println(i);// 输出0底层字节码解析JVM 视角编译器在处理i时会先将当前值0压入操作数栈临时存储然后给i加1i变成1最后将栈中的临时值0弹出并赋值给i。所以i经历了0→ 存入栈 → 自增成1→ 栈中0覆盖回i→ 最终0。操作操作数栈局部变量i初始空0加载i[0]0i自增[0]1赋值弹栈0被覆盖i的区别int i0; int ji;→ 先自增i1再压栈赋值j1。输出i1, j1。陷阱 2短路运算和||的“救人一命”短路与如果左边为false右边根本不执行。短路或||如果左边为true右边根本不执行。Stringstrnull;if(str!nullstr.length()0){// ✅ 安全左边 false右边不执行不会 NPE// ...}if(str!nullstr.length()0){// ❌ 危险非短路 两边都执行直接 NPE// ...}记忆口诀是“一假即假遇假就停”||是“一真即真遇真就停”。陷阱 3位运算右移vs符号位处理有符号右移左边补符号位正数补 0负数补 1。无符号右移左边统一补 0负数的二进制会变成很大的正数。inta-8;// 二进制11111111 11111111 11111111 11111000System.out.println(a1);// -4保留符号位除以 2 向下取整System.out.println(a1);// 2147483644左边补 0变成超大正数日常开发中常见于哈希算法、位图BitMap或底层网络报文解析。陷阱 4复合赋值运算符的“隐形强转”byteb127;b1;// ✅ 编译通过b 变成 -128发生溢出但语法正确bb1;// ❌ 编译报错Type mismatch: cannot convert from int to byte原理b 1等价于b (byte)(b 1)编译器自动插入了强制类型转换。而b b 1中b 1会先类型提升为int不能直接赋值给byte。警示虽然编译通过但溢出风险依然存在。127 1 -128这在业务中可能是灾难性的。陷阱 5运算符优先级中的“隐形炸弹”——移位低于加减这是 90% 的开发者都会答错的点intresult231;System.out.println(result);// 输出32错误推理(2 3) 1 16 1 17❌正确推理优先级高于所以2 (3 1) 2 4 32✅优先级速记口诀由高到低括号 单目--!~ 算术* / % - 移位 比较 instanceof 相等 ! 位逻辑^| 逻辑|| 三元? : 赋值陷阱 6与equals()的经典混淆联动之前的Integer缓存池基本类型int、char等比较的是数值本身。引用类型String、Integer等比较的是内存地址引用equals()比较的是内容。Integera100;Integerb100;System.out.println(ab);// true缓存池命中同一对象Integerc200;Integerd200;System.out.println(cd);// false超出 -128~127不同对象System.out.println(c.equals(d));// true内容相同铁律包装类比较数值永远用equals()不要用。陷阱 7instanceof与数组/接口的“奇怪行为”String[]arrnewString[10];System.out.println(arrinstanceofObject);// true数组是对象System.out.println(arrinstanceofObject[]);// trueSystem.out.println(arrinstanceofCloneable);// true数组实现了 CloneableSystem.out.println(arrinstanceofSerializable);// true数组实现了 Serializableinstanceof右边可以是类、接口、数组类型。数组在 JVM 中也是对象且有对应的类[Ljava.lang.String;。陷阱 8逻辑非!与按位取反~的区别!逻辑非只用于boolean!true→false。~按位取反用于整数将二进制每一位 0 变 1、1 变 0包括符号位。booleanflagtrue;System.out.println(!flag);// falseintnum5;// 二进制0000...0101System.out.println(~num);// -6二进制1111...1010即 -6陷阱 9除零异常——整数 vs 浮点数的差异inta1/0;// ❌ 运行时抛出 ArithmeticExceptiondoubleb1.0/0;// ✅ 输出 InfinityIEEE 754 规范doublec0.0/0;// ✅ 输出 NaNNot a Number浮点数运算遵循 IEEE 754 标准遇到除零不会抛异常而是返回特殊值Infinity、-Infinity、NaN。判断NaN要用Double.isNaN()不要用。三、位运算的实战场景让代码更优雅场景 1权限系统位掩码// 权限定义读1写2执行4publicstaticfinalintREAD1;// 001publicstaticfinalintWRITE2;// 010publicstaticfinalintEXEC4;// 100intpermissionREAD|WRITE;// 011拥有读和写权限booleancanRead(permissionREAD)!0;// truebooleancanExec(permissionEXEC)!0;// false// 追加权限permission|EXEC;// 011 | 100 111拥有全部权限// 移除权限permission~WRITE;// 111 ~010 101移除写权限场景 2高效乘除 2 的幂底层优化inta10;a1;// 相当于 a * 2 20比乘法快a1;// 相当于 a / 2 5比除法快// 但注意右移负数时向下取整除法向零取整intb-5;System.out.println(b1);// -3向下取整System.out.println(b/2);// -2向零取整四、JVM 字节码视角进阶运算符在编译后会变成 JVM 字节码指令。以int a 2 3;为例// Java 源码inta23;// 字节码javap 反编译iconst_2// 将常量 2 压入操作数栈iconst_3// 将常量 3 压入操作数栈iadd// 弹出栈顶两个 int相加后将结果压回栈顶istore_1// 将栈顶值弹出存储到局部变量表的槽位 1即变量 a运算符对应字节码指令部分-*/%intiadd、isub、imul、idiv、irem--intiinc直接在局部变量表自增无需压栈intif_icmpeq、if_icmpnebooleanifeq条件跳转实现短路ishl、ishr、iushr关键iinc指令是直接在局部变量表上操作的不经过操作数栈所以i的效率其实非常高。但i i多了一步istore覆盖导致了上面的陷阱。五、实战避坑总结场景推荐做法判空后再调用方法用短路运算不要用包装类/对象比较永远用equals()只用来比较基本类型或判断是否为null浮点数判断是否为 0用Math.abs(a) 1e-6不要用a 0权限控制/状态位用位运算符、复杂表达式加括号不要依赖优先级记忆让代码一眼就能看懂除以 2 的幂次优化用或代替*/但要注意负数取整的差异六、思考题检验是否真的懂了下面这段代码每一行的输出分别是什么为什么inti0;System.out.println(i);// 1System.out.println(i);// 2System.out.println(ii);// 3intj5;System.out.println(2j);// 4booleanflagfalse;System.out.println(flagtrue);// 5注意这里不是比较答案选中下方空白区域查看0先打印后自增2第一次输出后i1i先自增成2再打印2先自增再覆盖过程和陷阱 1 一样i先变 3 再被栈中 2 覆盖2 6 128j先算j6然后2 6true注意是赋值不是比较flag被赋值为true并打印出来 互动话题你在实际开发中有没有因为的前置后置、或者短路与非短路运算符搞混导致过线上 Bug欢迎评论区分享你的血泪史如果觉得有收获别忘了点赞、收藏、转发让更多 Javaer 彻底搞懂运算符的底层逻辑我们下篇见发布日期2026-08-16
返回列表