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

文章目录

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

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

java 复制代码
int math = 85;
int english = 92;
double avg = (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 解决方案

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

java 复制代码
double avg1 = (math + english) / 2.0;        // 88.5
double avg2 = (double)(math + english) / 2;   // 88.5

方案二: 分子分母都转:

java 复制代码
double avg3 = (math + english) * 1.0 / 2;    // 88.5

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


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

2.1 错误现象

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

java 复制代码
int score = 60;
if (score = 100) {   // ❌ 编译报错
    System.out.println("满分");
}

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

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

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

2.2 错误原因

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

2.3 解决方案

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

java 复制代码
// ❌ 如果误写成 if (score = 100),编译通过但逻辑错误
if (score == 100) { }

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

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


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

3.1 错误现象

java 复制代码
public class IncrementBug {
    public static void main(String[] args) {
        int x = 5;
        int y = x++;   // 以为 y = 6,实际上 y = 5
        System.out.println("x = " + x + ", y = " + y);
        // 输出:x = 6, y = 5
    }
}

3.2 错误原因

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

java 复制代码
int temp = x;   // temp = 5(保存 x 的当前值)
x = x + 1;      // x = 6
y = temp;       // y = 5

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

java 复制代码
int z = ++x;    // x 先变成 6,再赋给 z,z = 6

3.3 解决方案

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

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


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

4.1 错误现象

java 复制代码
int i = 1;
i = i++;
System.out.println(i);  // 输出:1(期望是2!)

4.2 错误原因

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

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

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

4.3 解决方案

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

java 复制代码
i++;          // ✅ 简洁明了
// 或
i = i + 1;    // ✅ 语义清晰

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

5.1 错误现象

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

5.2 错误原因

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

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

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

java 复制代码
a + b + "是结果"
→ 10 + 20 + "是结果"      // int + int → 加法
→ 30 + "是结果"            // int + String → 拼接
→ "30是结果"

5.3 解决方案

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

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

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


6 ~> 用 == 比较浮点数

6.1 错误现象

java 复制代码
double d1 = 0.1 + 0.2;
double d2 = 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 解决方案

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

java 复制代码
double epsilon = 0.0000001;
boolean isEqual = Math.abs(d1 - d2) < epsilon;
System.out.println(isEqual);  // 输出:true

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

java 复制代码
import java.math.BigDecimal;
BigDecimal bd1 = new BigDecimal("0.1");
BigDecimal bd2 = new BigDecimal("0.2");
BigDecimal result = bd1.add(bd2);  // 精确等于 0.3

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


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

7.1 错误现象

java 复制代码
int score = 80;
// 想用三元运算符赋不同提示语
String msg = (score >= 60) ? "及格" : 100;  // ❌ 编译报错

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

7.2 错误原因

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

7.3 解决方案

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

java 复制代码
String msg = (score >= 60) ? "及格" : "100";      // ✅ 两边都是 String
int grade = (score >= 60) ? 1 : 0;                // ✅ 两边都是 int

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

java 复制代码
String msg;
if (score >= 60) {
    msg = "及格";
} else {
    msg = "0";
}

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

8.1 错误现象

java 复制代码
short s = 1;
s = s + 1;   // ❌ 编译报错:incompatible types: possible lossy conversion from int to short
s += 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 解决方案

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

java 复制代码
short s = 1;

// 方案一:显式强转(清晰,推荐)
s = (short)(s + 1);

// 方案二:直接用复合赋值(简洁,但隐藏了转换)
s += 1;

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


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

9.1 错误现象

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

java 复制代码
int a = 5;
int b = 3;

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

9.2 错误原因

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

9.3 解决方案

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

java 复制代码
if (a > 10) {
    b++;                // 条件成立才自增
}
boolean result = (a > 10) ? true : false;

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

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

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


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

10.1 错误现象

java 复制代码
int result = 10 + 3 * 2;           // 输出:16(先乘后加,内心期望 26)
boolean flag = true || false && false;  // 输出:true(&& 优先级高于 ||)
int shift = 4 << 1 + 1;            // 输出:16(+ 优先级高于 <<,等价于 4 << 2)

10.2 错误原因

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

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

10.3 解决方案

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

java 复制代码
int result1 = 10 + (3 * 2);                // ✅ 明确:10 + 6 = 16
int result2 = (10 + 3) * 2;                // ✅ 明确:13 * 2 = 26
boolean flag = true || (false && false);    // ✅ 明确
int shift = 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. 优先级猜错: 不确定就加括号。括号不花钱也不减速,但能让你和后来看代码的人都省去猜优先级的功夫。

终极排查思路:

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

结尾

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

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

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

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

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

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

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

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

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

往期回顾:

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

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

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

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

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

相关推荐
San813_LDD3 小时前
[QT]Qt对象树笔记:父子关系与内存管理
开发语言·qt
gaohe26AIliuzeyu3 小时前
Java接口
java·开发语言
满怀冰雪3 小时前
第05篇-滑动窗口算法-一套模板解决子串与子数组问题
java·算法
小程故事多_803 小时前
RAGFlow 分块策略全景与 Book 策略深度解析
java·开发语言·rag
阿里嘎多学长3 小时前
2026-06-09 GitHub 热点项目精选
开发语言·程序员·github·代码托管
枫叶丹43 小时前
【HarmonyOS 6.0】MDM Kit 新增限制策略深度解析:短信、蜂窝数据、飞行模式、通知消息与 NFC 管控
开发语言·华为·harmonyos
吴声子夜歌3 小时前
JVM——线程池实现原理
java·jvm·线程池
雾沉川3 小时前
IntelliJ IDEA 2025.2 安装与基础配置技术教程
java·ide·intellij-idea
AC赳赳老秦3 小时前
技术文章素材收集自动化:用 OpenClaw 自动爬取行业资讯、技术热点、优质文章
运维·开发语言·python·自动化·wpf·deepseek·openclaw