当前位置: 首页 > news >正文

【3.1Java基础】Java运算符常见错误排查:10个高频编译运行错误一网打尽

文章目录

  • 【3.1Java基础】Java运算符常见错误排查:10个高频编译运行错误一网打尽
    • 导入语
    • 1 ~> 整数除法结果被截断
      • 1.1 错误现象
      • 1.2 错误原因
      • 1.3 解决方案
    • 2 ~> 把赋值 `=` 写成了比较 `==`(反之亦然)
      • 2.1 错误现象
      • 2.2 错误原因
      • 2.3 解决方案
    • 3 ~> 自增自减的"前置后置"搞混
      • 3.1 错误现象
      • 3.2 错误原因
      • 3.3 解决方案
    • 4 ~> 经典陷阱:`i = i++`
      • 4.1 错误现象
      • 4.2 错误原因
      • 4.3 解决方案
    • 5 ~> 字符串拼接误吃后面的数字
      • 5.1 错误现象
      • 5.2 错误原因
      • 5.3 解决方案
    • 6 ~> 用 `==` 比较浮点数
      • 6.1 错误现象
      • 6.2 错误原因
      • 6.3 解决方案
    • 7 ~> 三元运算符的返回值类型不一致
      • 7.1 错误现象
      • 7.2 错误原因
      • 7.3 解决方案
    • 8 ~> 复合赋值运算符的类型转换陷阱
      • 8.1 错误现象
      • 8.2 错误原因
      • 8.3 解决方案
    • 9 ~> 逻辑运算符短路导致的副作用漏掉
      • 9.1 错误现象
      • 9.2 错误原因
      • 9.3 解决方案
    • 10 ~> 运算符优先级导致结果出人意料
      • 10.1 错误现象
      • 10.2 错误原因
      • 10.3 解决方案
    • 思考 && 总结
    • 结尾

【3.1Java基础】Java运算符常见错误排查:10个高频编译运行错误一网打尽

📖文章简介:本文是Java运算符的专项错误排查手册,整理了初学者在使用运算符时最高频的10个编译/运行错误。每个错误均采用"错误现象→错误原因→解决方案"三段式拆解,覆盖了整除截断、===混淆、自增自减陷阱、字符串拼接意外、浮点数相等判断、三元运算符类型不匹配、复合赋值隐藏的类型转换、除零异常、逻辑短路导致的副作用遗漏、以及括号缺失引发的优先级翻车。文末附三连击排查思路,适合刚学完运算符但总是编译报错的同学对照排查。


🎬 个人主页:源码骑士

专栏传送门:《java编程练习题》《全栈开发》

⭐️热衷从源码视角拆解技术底层原理,将复杂架构讲得通俗易懂


🎬 源码骑士的简介:
5年Android Framework系统开发经验,曾主导多项系统级性能优化专项
技术栈覆盖Android系统全链路(Binder/Handler/AMS/WMS/启动流程)及Java后端全家桶(Spring + MyBatis + Redis + Oracle)
累计产出原创技术文章100+篇,文章以流程图为特色,被读者评价为"看一篇胜过啃一周核心竞争力"


导入语

学完运算符之后,你兴冲冲地打开 Idea 准备写几个运算练手。结果刚敲了几行,要么编译器标红一片,要么程序跑起来输出跟你想的完全不一样。

这不是你的问题。运算符看似简单(加减乘除谁不会?),但 Java 的强类型加上一些语言特性,确实挖了不少坑。这篇文章就是专门来填这些坑的。每个错误都用"现象→原因→解决方案"拆开来讲,看完之后,你拿到一份报错信息就知道怎么排查。


1 ~> 整数除法结果被截断

1.1 错误现象

写了一段代码想算平均值,结果怎么算都是整数:

intmath=85;intenglish=92;doubleavg=(math+english)/2;System.out.println("平均分:"+avg);// 输出:88.0(期望88.5)

1.2 错误原因

两个整数相除,结果还是整数。小数部分直接截断,不会四舍五入。(85 + 92) / 2 = 177 / 2 = 88,整数 88 赋给 double 变量后才变成 88.0,但小数部分早就丢掉了。

1.3 解决方案

方案一:让其中一个操作数变成浮点数:

doubleavg1=(math+english)/2.0;// 88.5doubleavg2=(double)(math+english)/2;// 88.5

方案二:分子分母都转:

doubleavg3=(math+english)*1.0/2;// 88.5

记住:除号两边只要有一个浮点数,就是小数除法。


2 ~> 把赋值=写成了比较==(反之亦然)

2.1 错误现象

现象A:条件判断时不小心用了=

intscore=60;if(score=100){// ❌ 编译报错System.out.println("满分");}

报错信息:incompatible types: int cannot be converted to boolean

现象B:赋值的意图却写了==(通常不会报错,但逻辑完全错误):

booleanflag=(score==100);// 意图是"score 等于100吗?"✅flag==true;// 想重新赋值 flag = true,但这里只做了比较,结果丢弃了System.out.println(flag);// 输出:false(flag 根本没变)

2.2 错误原因

=是赋值运算符,把右边的值放进左边的变量。==是比较运算符,判断两边是否相等,结果是 boolean 类型。两者键盘上紧挨着,手快就打错了。

2.3 解决方案

写条件判断时,把常量放在前面,这样即使误写成=编译器也会报错:

// ❌ 如果误写成 if (score = 100),编译通过但逻辑错误if(score==100){}// ✅ 把常量放前面:如果误写成 if (100 = score),编译器直接报错if(100==score){}

这个写法叫"Yoda 条件",常量在前,变量在后,打错时编译器救你。


3 ~> 自增自减的"前置后置"搞混

3.1 错误现象

publicclassIncrementBug{publicstaticvoidmain(String[]args){intx=5;inty=x++;// 以为 y = 6,实际上 y = 5System.out.println("x = "+x+", y = "+y);// 输出:x = 6, y = 5}}

3.2 错误原因

这是初学者踩得最多的坑。x++(后置)的执行顺序是先用 x 当前的值参与运算,再把 x 加 1。所以y = x++等价于:

inttemp=x;// temp = 5(保存 x 的当前值)x=x+1;// x = 6y=temp;// y = 5

如果用的是++x(前置),结果才对:

intz=++x;// x 先变成 6,再赋给 z,z = 6

3.3 解决方案

你的意图正确写法错误写法
先加 1,再赋值y = ++x;y = x++;
先赋值,再加 1y = x++;y = ++x;
不确定,求稳x++;
y = x;
别写一行

初学阶段最简单的避坑法:把自增自减单独写一行,不要和赋值混在一起。x++; y = x;两行清清楚楚。


4 ~> 经典陷阱:i = i++

4.1 错误现象

inti=1;i=i++;System.out.println(i);// 输出:1(期望是2!)

4.2 错误原因

这是前一个问题的升级版。i = i++的执行步骤非常反直觉:

1.先把 i 的当前值(1)压入操作数栈(Java虚拟机的临时存储区)2.i 自增为23.赋值:i=操作数栈中保存的值(14.最终 i 又变回了1

Java 中++的优先级高于=,但后置自增的"先用后加"特性让赋值发生在自增之后,而赋的值又是自增前的旧值。这一套组合拳打下来,值就不对了。

4.3 解决方案

永远不要写i = i++。如果你想让i自增:

i++;// ✅ 简洁明了// 或i=i+1;// ✅ 语义清晰

5 ~> 字符串拼接误吃后面的数字

5.1 错误现象

inta=10,b=20;System.out.println("结果:"+a+b);// 输出:结果:1020(期望:结果:30)System.out.println(a+b+"是结果");// 输出:30是结果(这个对了)

5.2 错误原因

+运算符遇到 String 时,行为会从加法变成字符串拼接。而且拼接是从左往右依次执行的:

"结果:"+a+b →"结果:"+10+20// 字符串 + int → 字符串拼接"结果:10"+20// 字符串 + int → 仍然是拼接"结果:1020"

反过来,先算a + b就不一样了:

a+b+"是结果"10+20+"是结果"// int + int → 加法30+"是结果"// int + String → 拼接"30是结果"

5.3 解决方案

只要用括号把先算的部分括起来:

System.out.println("结果:"+(a+b));// 输出:结果:30System.out.println(a+" + "+b+" = "+(a+b));// 输出:10 + 20 = 30

一句话:+两边有一个是 String 就拼接,全是数字就加法。不确定就加括号。


6 ~> 用==比较浮点数

6.1 错误现象

doubled1=0.1+0.2;doubled2=0.3;System.out.println(d1==d2);// 输出:false(期望 true)

6.2 错误原因

浮点数在计算机里是以二进制形式存储的,而十进制的0.10.2在二进制中是无限循环小数,只能存储近似值。所以0.1 + 0.2实际存储的是0.30000000000000004,跟0.3有一丁点偏差,==比较自然为false

6.3 解决方案

方案一:比较差值是否小于一个很小的阈值:

doubleepsilon=0.0000001;booleanisEqual=Math.abs(d1-d2)<epsilon;System.out.println(isEqual);// 输出:true

方案二:涉及金额的精确计算用BigDecimal(后面学到再细讲):

importjava.math.BigDecimal;BigDecimalbd1=newBigDecimal("0.1");BigDecimalbd2=newBigDecimal("0.2");BigDecimalresult=bd1.add(bd2);// 精确等于 0.3

初学阶段记住:“浮点数不要用==比较”,就够了。


7 ~> 三元运算符的返回值类型不一致

7.1 错误现象

intscore=80;// 想用三元运算符赋不同提示语Stringmsg=(score>=60)?"及格":100;// ❌ 编译报错

报错信息:incompatible types: bad type in conditional expression

7.2 错误原因

三元运算符的?后面的两个结果值必须是兼容的类型。上面代码中一个是String,一个是int(100),编译器不知道整个表达式的类型是什么,直接报错。

7.3 解决方案

让两个结果值的类型保持一致:

Stringmsg=(score>=60)?"及格":"100";// ✅ 两边都是 Stringintgrade=(score>=60)?1:0;// ✅ 两边都是 int

如果确实需要不同类型的结果,换用if-else

Stringmsg;if(score>=60){msg="及格";}else{msg="0";}

8 ~> 复合赋值运算符的类型转换陷阱

8.1 错误现象

shorts=1;s=s+1;// ❌ 编译报错:incompatible types: possible lossy conversion from int to shorts+=1;// ✅ 编译通过

奇怪:s += 1s = s + 1不是等价的吗?

8.2 错误原因

它们语义上等价,但编译器处理方式不同

  • s = s + 1:右边s + 1运算时,s先自动提升为 int,加完结果是 int,不能直接赋给 short,报错。
  • s += 1:Java 语言规范规定,复合赋值运算符隐式包含强制类型转换,相当于s = (short)(s + 1)

8.3 解决方案

两种写法都可以,但要理解它们的行为差异:

shorts=1;// 方案一:显式强转(清晰,推荐)s=(short)(s+1);// 方案二:直接用复合赋值(简洁,但隐藏了转换)s+=1;

如果运算可能导致溢出(比如 short 加到超过 32767),用显式强转更安全,至少你知道自己在冒什么风险。


9 ~> 逻辑运算符短路导致的副作用漏掉

9.1 错误现象

想用短路特性链式调用,结果第二个操作数根本没执行:

inta=5;intb=3;booleanresult=(a>10)&&(b++>0);// 期望 b 自增为 4System.out.println("b = "+b);// 输出:b = 3(b++ 根本没执行!)

9.2 错误原因

&&的短路特性:如果左边是false,右边直接跳过。因为a > 10false,所以b++根本没机会执行。同理,||左边为true时右边也跳过。

9.3 解决方案

不要把有"副作用"(改变变量值)的操作写在逻辑运算符里。如果确实需要根据条件修改值,拆开来写:

if(a>10){b++;// 条件成立才自增}booleanresult=(a>10)?true:false;

一种利用短路特性的正确实战场景——判空保护:

Stringname=null;// 短路保证 name 为 null 时,不会执行 equals()if(name!=null&&name.equals("admin")){System.out.println("管理员登录");}

原则:短路特性只用来做预判保护(!= null &&),不要在里面偷偷改变量值。


10 ~> 运算符优先级导致结果出人意料

10.1 错误现象

intresult=10+3*2;// 输出:16(先乘后加,内心期望 26)booleanflag=true||false&&false;// 输出:true(&& 优先级高于 ||)intshift=4<<1+1;// 输出:16(+ 优先级高于 <<,等价于 4 << 2)

10.2 错误原因

Java 的运算符优先级很细,人脑很难全部记全。上面三个例子:

表达式实际执行顺序结果可能误以为
10 + 3 * 210 + (3 * 2)16(10 + 3) * 2 = 26
true || false && falsetrue || (false && false)true(true || false) && false = false
4 << 1 + 14 << (1 + 1)16(4 << 1) + 1 = 9

10.3 解决方案

写一个铁律:拿不准优先级的时候,加括号。括号里的先算,这个规则在全世界所有编程语言里都成立。加括号不丢人,也不影响性能:

intresult1=10+(3*2);// ✅ 明确:10 + 6 = 16intresult2=(10+3)*2;// ✅ 明确:13 * 2 = 26booleanflag=true||(false&&false);// ✅ 明确intshift=4<<(1+1);// ✅ 明确:4 << 2 = 16

思考 && 总结

本文拆解了 Java 运算符使用过程中最高频的 10 个编译/运行错误,核心排查思路如下:

  1. 整数除法截断:两个整数相除结果还是整数,小数点后面直接砍掉。让除数或被除数变成浮点数(如/ 2.0)即可得到小数结果。
  2. ===混淆:=是赋值,==是比较。条件判断用"Yoda 写法"(常量在前)可让编译器帮你抓出笔误。
  3. 自增自减前后置:前置++x先加后用,后置x++先用后加。初学阶段把自增单独写一行,别跟赋值混一起。i = i++永远不要写。
  4. 字符串拼接吞数字:"结果:" + a + b从左往右依次拼接,a+b变成字符串拼接而不是加法。加括号(a+b)解决。
  5. 浮点数别用==二进制存储的精度问题导致0.1 + 0.2 != 0.3。用差值比较替代等于判断,精度敏感场景用BigDecimal
  6. 三元运算符类型一致性:?后面的两个返回值类型必须兼容,一个 String 一个 int 编译直接报错。
  7. 复合赋值的隐式转换:s += 1包含自动强制转换,s = s + 1不包含。知道这个差异就行,日常用显式强转更安全。
  8. 短路导致副作用遗漏:&&左边 false 右边不执行。利用短路做!= null保护是好事,但不要把改变量值的操作藏在右边。
  9. 优先级猜错:不确定就加括号。括号不花钱也不减速,但能让你和后来看代码的人都省去猜优先级的功夫。

终极排查思路:

拿到一个运算结果不对的问题 ├─ 第一步:看有没有类型提升(整数除法?int + double?) ├─ 第二步:看有没有字符串参与(+ 拼接了?顺序对吗?) ├─ 第三步:看有没有自增自减(前置还是后置?跟赋值写在一起了吗?) ├─ 第四步:看有没有优先级坑(加个括号再跑一次试试?) └─ 第五步:看有没有短路逻辑(&&||右边的代码实际执行了吗?)

结尾

各位小伙伴,本文的内容到这里就全部结束了,源码骑士在这里再次感谢您的阅读!

源码骑士 — Android Framework & 全栈开发

👀关注:跟博主一起从源码视角深耕底层原理,见证每一次成长

❤️点赞:让优质内容被更多人看见,让知识传递更有力量

收藏:把核心知识点存好,在需要时随时查、随时用

💬评论:分享你的经验或疑问,评论区一起交流避坑

🔄一键四连:不要忘记给博主"一键四连"哦!今日源码拆解达成!

🗡️寄语:技术之路难免有困惑,但同行的人会让前进更有方向

结语:编译报错不是你的敌人,是编译器在帮你找 bug。希望这篇文章能帮你拿下运算符阶段的所有编译运行错误。不要忘记给博主"一键四连"哦!

往期回顾:

【1.Java基础】Java初识:从零搭建开发环境到写出第一个HelloWorld

【1.1Java基础】JDK安装常见问题教辅-从踩坑到排雷

【1.2Java基础】Win10环境变量配置详解-从原理到排雷

【2.Java基础】Java常量与变量-从基本类型到类型转换全面掌握

【3.Java基础】Java运算符详解:从算数运算到逻辑判断

http://www.cnnetsun.cn/news/2855508.html

相关文章:

  • 还在用老版本jQuery?手把手教你复现CVE-2020-11022/11023这个XSS漏洞(附完整PoC)
  • 别再死记公式!用Python模拟带你直观理解停止等待与回退N帧协议的信道利用率
  • 考研摆烂后如何一周突击复试?北邮网安复试准备全流程(含密码学、408速成法)
  • 新手避坑指南:用大疆NAZA-LITE飞控组装F450无人机,从焊接电调到GPS校准的完整流程
  • ARM9微控制器LPC292x硬件设计实战:从数据手册到可靠电路
  • 从一次线上数据泄露事故复盘:我们是如何用签名和脱敏堵住越权漏洞的
  • 工业数据上云的‘翻译官’:实测KepOPC DA2UA如何桥接Windows OPC DA与跨平台应用
  • 别再傻傻分不清!用猫狗猪分类的例子,一次搞懂论文里的OA、mAcc、Instance和Class Accuracy
  • 动态群组密钥管理协议:原理、实现与优化
  • 不只是玩具:用金牛座脑波模块+ESP32,打造一个低成本的居家专注力监测‘小黑盒’
  • 告别盲目搜索:手把手教你用Keil MDK调试RT-Thread的RT_ASSERT死机问题
  • Arma3任务制作者必看:如何用SQF的ForEach和WaitUntil,让AI小队执行复杂巡逻逻辑
  • 语音RAG实战:构建端到端音频理解与原声回答系统
  • 告别IP依赖:在Vivado中直接调用MMCME2_ADV原语生成自定义时钟(以Zynq-7000为例)
  • 从零配置到上线:手把手带你用华为AC+AP搭建一个可用的企业Wi-Fi(含CAPWAP隧道详解)
  • 别让DRC吓到你!Cadence SPB17.4原理图检查的‘白名单’与‘黑名单’设置心得
  • 别再套模板了!我用这3个真实案例拆解GIS/遥感专业保研个人陈述怎么写(附避坑指南)
  • 别再用暴力搜索了!用动态规划5分钟搞定‘蚂蚁移动’这类网格路径问题(附C++代码)
  • 上市公司财报AI解析流水线:本地化、可验证、零API依赖
  • 用C++队列模拟流感传播:从NOI真题到游戏地图感染算法实战
  • AI简历优化:三重信号编码法突破ATS筛选
  • 别再只看GPS信号格了!手把手教你读懂手机/车载导航里的DOP值(精度衰减因子)
  • 别再死磕TII投稿了!我用LaTeX搞定IEEE论文格式的血泪经验(附模板下载与避坑清单)
  • OpenLayers测距踩坑记:从EPSG:4326坐标偏差到Vue中内存泄漏的排查与修复
  • GeoServer权限进阶:不用账号密码,用AuthKey插件实现API密钥式鉴权(2.25.2 Docker版)
  • 模板驱动型文档自动化:结构化内容生成的核心原理与实践
  • 你的Vue/React老项目可能中招了!排查并修复jQuery 3.5.0以下版本的XSS隐患
  • Android系统定制:如何隐藏开发者模式入口,并用计算器输入%147%+来开启(附完整代码)
  • NXP LPC55S6x双核MCU实战:从TrustZone安全到低功耗设计
  • 深入解读S32K3的SAF安全状态机:mSel模块如何决定MCU是“正常运行”还是“立刻复位”?