运算符 | JavaSE

运算符

一、学习目标

完成本章后,你应该能够:

  • 能够理解运算符(Operator)与表达式(Expression)的基本关系。
  • 能够熟练使用算术运算符,并正确分析整数除法、取余运算和字符串拼接。
  • 能够区分前置自增、自减与后置自增、自减。
  • 能够正确使用基本赋值运算符和复合赋值运算符。
  • 能够使用关系运算符和逻辑运算符构造布尔条件。
  • 能够解释 &&|| 的短路求值机制。
  • 能够正确使用三元运算符处理简单二选一逻辑。
  • 能够根据运算符优先级和结合方向分析表达式,并知道什么时候应该主动使用括号提高代码可读性。

二、核心知识

2.1 什么是运算符

运算符(Operator)可以理解为:

对一个或多个数据执行某种运算的特殊符号。

例如:

int result = 10 + 20;

其中:

10
20

是操作数(Operand)。

+

是运算符。

而:

10 + 20

整体叫做表达式(Expression)。

表达式计算完成后会产生一个结果:

30

Java 中常见运算符包括:

| 类型 | 常见运算符 | | ---------- | ------------------ | | 算术运算符 | + - * / % | | 自增自减 | ++ -- | | 赋值运算符 | = += -= *= /= %= | | 关系运算符 | > < >= <= == != | | 逻辑运算符 | `& | | 条件运算符 | ? : |

这一章的核心,不只是记住符号,而是掌握:

运算符作用
+
操作数类型
+
运算顺序
+
表达式结果类型

2.2 算术运算符

Java 中最基本的算术运算符:

| 运算符 | 含义 | | ------ | ---- | | + | 加 | | - | 减 | | * | 乘 | | / | 除 | | % | 取余 |

例如:

int a = 10;
int b = 3;

System.out.println(a + b);
System.out.println(a - b);
System.out.println(a * b);
System.out.println(a / b);
System.out.println(a % b);

分别表示:

10 + 3
10 - 3
10 × 3
10 ÷ 3
10 除以 3 的余数

2.3 整数除法

这是 Java 初学阶段非常重要的规则。

System.out.println(5 / 2);

结果不是:

2.5

而是:

2

原因在于:

int / int

执行的是整数除法。

两个操作数都是 int

5
2

结果类型仍然是整数类型,因此小数部分不会被保留。

如果希望得到:

2.5

至少需要让一个操作数进入浮点运算。

例如:

System.out.println(5.0 / 2);

或者:

System.out.println((double) 5 / 2);

都可以得到:

2.5

所以不要只盯着:

数学结果应该是什么

还必须考虑:

Java 当前按照什么数据类型执行运算

2.4 % 取余运算符

% 表示余数运算。

例如:

System.out.println(10 % 3);

得到:

1

因为:

10 ÷ 3 = 3 …… 1

取余运算在程序中非常常见。

例如判断奇偶数:

int number = 10;

boolean even = number % 2 == 0;

System.out.println(even);

还可以用于:

周期性处理
数字拆分
循环轮转
奇偶判断
时间换算
下标循环

例如:

int number = 123;

int ones = number % 10;

就可以得到个位数字。


2.5 + 还可以进行字符串拼接

Java 中:

+

并不永远代表数学加法。

+ 的操作数中存在 String 时,它可以用于字符串拼接。

例如:

String name = "星雨";
int age = 22;

System.out.println(name + age);

得到:

星雨22

这里:

+

不是把字符串和整数做数学加法,而是把数据转换成字符串形式后连接起来。


2.6 连续 + 为什么容易出错

观察:

int a = 5;

System.out.println("abc" + a);
System.out.println(a + 5);
System.out.println("itheima" + a + 'a');
System.out.println(a + 'a' + "itheima");

理解这种代码的关键是:

+ 按照从左到右进行结合。

例如:

a + 'a' + "itheima"

首先计算:

a + 'a'

由于:

a 是 int
'a' 可以参与整数运算

所以:

5 + 97 = 102

然后:

102 + "itheima"

才发生字符串拼接。

最终得到:

102itheima

而:

"itheima" + a + 'a'

首先执行:

"itheima" + 5

已经变成字符串:

"itheima5"

随后:

"itheima5" + 'a'

继续进行字符串拼接:

itheima5a

因此初学阶段可以用一句话帮助理解:

从左到右,能做数值运算时先做数值运算;一旦进入字符串拼接,后续 + 通常继续按照字符串连接处理。

但是更准确的依据仍然是:

当前这一对 + 操作数的类型是什么。


2.7 自增与自减运算符

Java 提供:

++
--

用于将变量自身增加或减少 1

例如:

int count = 10;

count++;

System.out.println(count);

得到:

11

等价思想可以理解为:

count = count + 1;

同理:

count--;

表示:

count = count - 1;

2.8 ++ 写在前面还是后面

可以写:

a++;

也可以写:

++a;

如果单独作为一条语句:

a++;

与:

++a;

最终都会使:

a 增加 1

例如:

int a = 10;

a++;

System.out.println(a);

和:

int a = 10;

++a;

System.out.println(a);

都会输出:

11

真正的区别出现在:

++ 本身被放进一个更大的表达式中。


2.9 后置自增:先用后加

例如:

int a = 10;

int b = a++;

理解过程:

① 先使用 a 当前的值 10
② 将 10 赋给 b
③ a 再增加 1

最终:

a = 11
b = 10

可以记:

a++ → 先用后加

2.10 前置自增:先加后用

例如:

int a = 10;

int b = ++a;

过程:

① a 先增加 1
② a 变成 11
③ 再把 11 作为表达式结果
④ 赋给 b

最终:

a = 11
b = 11

可以记:

++a → 先加后用

-- 的规则完全对应:

a-- → 先用后减
--a → 先减后用

2.11 工程代码不要滥用复杂自增表达式

虽然 Java 允许:

int result = ++a + b++ - --c;

但是这类代码通常非常难读。

能够写出来:

应该写出来。

工程开发更强调:

正确
+
清晰
+
容易维护

而不是炫技。

因此对于复杂变量变化,宁可拆开:

a++;
b++;

int result = a + b;

也不要故意制造难以理解的表达式。


2.12 基本赋值运算符

Java 最基本的赋值运算符:

=

例如:

int age = 18;

这里并不是数学意义中的:

age 等于 18

而是:

将右侧计算得到的数据保存到左侧变量中。

所以:

age = age + 1;

完全合法。

执行过程应该从右往左理解:

① 读取 age 当前值
② 计算 age + 1
③ 将新结果重新赋值给 age

2.13 === 不一样

这一点必须彻底区分。

=

是赋值:

int age = 18;

而:

==

是比较是否相等:

boolean result = age == 18;

结果是:

true

所以:

=   → 赋值
==  → 判断是否相等

2.14 复合赋值运算符

Java 提供:

+=
-=
*=
/=
%=

例如:

int money = 100;

money += 20;

表示:

money 增加 20

对于普通 int 场景,可以粗略理解为:

money = money + 20;

同理:

money -= 10;
money *= 2;
money /= 5;
money %= 3;

分别表示:

减
乘
除
取余

2.15 复合赋值不只是简单文本替换

这一点与上一章类型转换直接相关。

例如:

byte value = 10;

value += 20;

这段代码可以编译。

但是:

byte value = 10;

// value = value + 20;

后者不能直接编译。

为什么?

因为:

byte + int

进行数值提升后:

value + 20

结果是:

int

而:

int → byte

不能直接隐式赋值。

复合赋值:

value += 20;

则包含了向左侧变量类型转换回去的语义。

可以用近似思想理解为:

value = (byte) (value + 20);

所以复合赋值不仅是:

少写几个字符

它还涉及 Java 语言定义的类型转换规则。

这也意味着:

复合赋值同样可能发生数据范围或精度问题。


2.16 关系运算符

关系运算符用于比较数据之间的关系。

常见:

| 运算符 | 含义 | | ------ | -------- | | > | 大于 | | < | 小于 | | >= | 大于等于 | | <= | 小于等于 | | == | 等于 | | != | 不等于 |

例如:

int score = 80;

System.out.println(score >= 60);

结果:

true

关系表达式最终产生:

boolean

也就是:

true

或者:

false

例如:

int age = 18;

boolean adult = age >= 18;

这里:

age >= 18

就是一个布尔表达式。


2.17 逻辑运算符

一个条件往往还不够。

例如:

手机价格必须大于等于 4000,并且小于等于 6000。

这需要组合两个条件:

price >= 4000

和:

price <= 6000

于是需要逻辑运算符。

常见逻辑运算符:

| 运算符 | 含义 | | ------ | -------- | | & | 逻辑与 | | ` | ` | | ^ | 逻辑异或 | | ! | 逻辑非 | | && | 短路与 | | ` | |


2.18 逻辑与 &

对于布尔值:

true & true   → true
true & false  → false
false & true  → false
false & false → false

即:

两边都是真,结果才是真。

例如:

boolean result = age >= 18 & age <= 60;

2.19 逻辑或 |

规则:

true | true   → true
true | false  → true
false | true  → true
false | false → false

即:

至少有一边是真,结果就是真。


2.20 逻辑异或 ^

对于布尔值:

true ^ true   → false
false ^ false → false
true ^ false  → true
false ^ true  → true

可以理解为:

两边不同才为 true。


2.21 逻辑非 !

! 用于取反。

例如:

boolean login = true;

System.out.println(!login);

结果:

false

规律:

!true  → false
!false → true

2.22 &&:短路与

&&& 在布尔逻辑结果上类似:

两边都为 true,整体才为 true。

但是:

&&

具有短路特性。

例如:

boolean result = false && someExpression;

左边已经是:

false

无论右边是什么:

false && ?

整体都不可能变成 true

所以:

Java 不再计算右侧表达式。

这就叫:

短路求值(Short-Circuit Evaluation)。

例如:

int number = 0;

boolean result = false && (++number > 0);

System.out.println(number);

右侧:

++number

不会执行。

所以:

number

仍然是:

0

2.23 ||:短路或

|| 同样具有短路特性。

例如:

boolean result = true || someExpression;

左侧已经:

true

那么:

true || ?

无论右边是什么,整体一定都是:

true

因此 Java 不再计算右侧。


2.24 &&& 的核心区别

对于布尔表达式:

&
&&

最终逻辑结果通常相同。

但是执行过程不同:

&  → 左右两边通常都计算
&& → 左侧 false 时,右侧不再计算

同理:

|  → 左右两边通常都计算
|| → 左侧 true 时,右侧不再计算

实际业务条件判断中:

&&
||

使用更加普遍。

不仅能够减少不必要计算,还经常可以避免右侧执行危险操作。


2.25 一个经典短路案例

例如:

int denominator = 0;

boolean valid = denominator != 0
        && 100 / denominator > 2;

先计算:

denominator != 0

结果:

false

因为使用:

&&

右侧:

100 / denominator

就不会执行。

这种写法展示了短路逻辑的重要价值:

后一个条件可以建立在前一个条件已经成立的前提之上。


2.26 三元运算符

Java 提供条件运算符:

? :

基本格式:

条件 ? 条件成立时的结果 : 条件不成立时的结果

例如:

int age = 20;

String result = age >= 18 ? "成年人" : "未成年人";

System.out.println(result);

执行逻辑:

age >= 18 ?
     ↓
   true
     ↓
"成年人"

如果条件为 false

选择冒号后面的表达式

2.27 三元运算符适合什么场景

三元运算符适合:

简单的二选一结果。

例如:

int a = 10;
int b = 20;

int max = a > b ? a : b;

或者:

int number = 10;

String type = number % 2 == 0 ? "偶数" : "奇数";

但是如果逻辑非常复杂:

多个条件
多条语句
大量业务操作
多级分支

就不应该为了“代码短”而强行套三元运算。

后面学习 if 后,应根据场景选择更加清晰的方案。


三、使用方法

3.1 算术运算完整示例

public class ArithmeticDemo {
    public static void main(String[] args) {
        int a = 10;
        int b = 3;

        System.out.println(a + b);
        System.out.println(a - b);
        System.out.println(a * b);
        System.out.println(a / b);
        System.out.println((double) a / b);
        System.out.println(a % b);
    }
}

重点观察:

a / b

与:

(double) a / b

之间的区别。


3.2 点赞数量变化

public class LikeDemo {
    public static void main(String[] args) {
        int count = 0;

        count++;
        count++;
        count++;

        System.out.println("点赞数:" + count);

        count--;

        System.out.println("取消点赞后:" + count);
    }
}

3.3 手机价格范围判断

public class PriceDemo {
    public static void main(String[] args) {
        double price = 5000;

        boolean suitable =
                price >= 4000 && price <= 6000;

        System.out.println(suitable);
    }
}

逻辑模型:

价格 >= 4000
      &&
价格 <= 6000

3.4 判断奇偶

public class EvenDemo {
    public static void main(String[] args) {
        int number = 25;

        String result =
                number % 2 == 0 ? "偶数" : "奇数";

        System.out.println(result);
    }
}

这里综合使用了:

%
==
? :

3.5 两个数取最大值

public class MaxDemo {
    public static void main(String[] args) {
        int a = 20;
        int b = 30;

        int max = a > b ? a : b;

        System.out.println(max);
    }
}

四、原理与进阶

4.1 表达式计算必须同时关注值和类型

学习运算符时最容易出现一个错误:

只算数值,不判断类型。

例如:

byte a = 10;
byte b = 20;

// byte result = a + b;

数学上:

10 + 20 = 30

但 Java 还需要考虑:

a + b 的表达式类型是什么?

根据数值提升规则:

byte + byte

会按照:

int + int

计算。

因此表达式类型是:

int

所以分析 Java 表达式应该建立完整思维:

操作数
 ↓
操作数类型
 ↓
是否发生类型提升
 ↓
执行什么运算
 ↓
得到什么值
 ↓
结果是什么类型

4.2 + 的真正判断标准是类型

有些入门教程会说:

能算则算,不能算就连接。

这个说法适合快速入门,但不够严谨。

更准确的规则是:

如果二元 + 的某个操作数是 String

字符串拼接

否则操作数必须能够参与数值运算:

数值加法

例如:

1 + 2 + "3"

按照从左到右:

1 + 2
↓
3

然后:

3 + "3"
↓
"33"

而:

"1" + 2 + 3

首先:

"1" + 2
↓
"12"

然后:

"12" + 3
↓
"123"

4.3 复合赋值只计算左侧一次

对于:

E1 op= E2

Java 的语义不仅包含类型转换,而且左侧表达式只会被计算一次。

对于初学阶段的普通变量:

a += b;

不容易看出差异。

但随着以后学习:

数组
对象
方法调用

这条规则会变得更加重要。

目前需要掌握的核心是:

+=

不能机械地认为只是把:

a = a +

缩写了一下。


4.4 ++-- 会写回原变量

例如:

int a = 10;

int b = a++;

这里实际上存在两个结果:

表达式 a++ 的结果 → 10
变量 a 更新后的结果 → 11

因此:

表达式的值

和:

变量最终保存的值

必须区分。

这是理解前置和后置自增最关键的一点。


五、实践应用

真实业务中的大量条件,本质上都是运算符组合。

例如登录条件:

boolean allowLogin =
        accountExists
        && passwordCorrect
        && !accountLocked;

商品筛选:

boolean match =
        price >= minPrice
        && price <= maxPrice;

权限判断:

boolean allow =
        isAdmin || isOwner;

数据校验:

boolean legal =
        age >= 0 && age <= 150;

因此:

运算符是后续 ifswitch、循环、算法和业务逻辑的基础语言。

如果连表达式都分析不清楚,后面流程控制就很容易出现“代码能看懂,但自己写条件总出错”的情况。


六、常见问题

6.1 为什么 5 / 2 不是 2.5

因为:

int / int

执行整数运算。

应该让至少一侧进入浮点类型:

5.0 / 2

或者:

(double) 5 / 2

6.2 % 只能用于判断奇偶吗?

不是。

奇偶判断只是最常见的应用之一。

% 还可以用于:

周期
轮转
数字拆分
倍数判断
时间换算

6.3 a++++a 哪个更快?

不应该以这种方式选择。

现代 Java 编译与运行环境下,真正应该考虑的是:

你需要“先使用旧值”还是“先得到新值”。

如果只是独立执行:

a++;

或:

++a;

重点是代码语义,而不是幻想中的性能差距。


6.4 a += 1a = a + 1 完全一样吗?

对于普通 int 场景结果经常相同,但语言语义不完全相同。

尤其对于:

byte
short
char

复合赋值包含向左侧变量类型转换回去的规则。


6.5 &&& 应该用哪个?

普通业务布尔条件通常优先:

&&

因为它具有短路能力。

但两者并不是同一个运算符。

另外,&|^ 还可以用于整数的按位运算,这部分在真正遇到位运算场景时再深入学习。


6.6 == 能不能比较字符串内容?

不要把:

==

简单理解为所有对象的“内容相等”。

对于基本数据类型,本章可以用它比较数值或布尔值。

对于 String 等引用类型:

==

涉及引用比较。

字符串内容比较将在 String 章节系统学习。


6.7 三元运算符一定比 if 好吗?

不是。

三元运算符适合:

条件 ? 值1 : 值2

这种简单二选一表达式。

复杂业务逻辑更应该关注:

清晰
可读
容易维护

而不是单纯追求代码短。


6.8 运算符优先级需要全部背下来吗?

没有必要机械背完整优先级表。

常用规则应该逐渐熟悉,例如:

算术
比较
逻辑
赋值

但只要表达式已经复杂到让人犹豫:

主动加括号。

例如:

boolean result =
        (score >= 60)
        && (attendance >= 80);

比依靠读者背优先级更加可靠。


七、练习与验收

7.1 知识问答

  1. Java 的算术运算符有哪些?
  2. 为什么 5 / 2 的结果是 2
  3. 如何让 5 / 2 得到 2.5?至少写出两种方式。
  4. % 运算符有哪些典型用途?
  5. + 在什么情况下表示字符串拼接?
  6. 为什么 "abc" + 1 + 21 + 2 + "abc" 的结果不同?
  7. ++aa++ 单独成句时有什么共同点?
  8. ++aa++ 放入表达式时有什么区别?
  9. === 分别表示什么?
  10. +=-=*=/=%= 分别表示什么?
  11. 为什么 byte b = 10; b += 20; 可以编译?
  12. 关系运算符最终产生什么类型的数据?
  13. &&& 有什么区别?
  14. ||| 有什么区别?
  15. ^ 对布尔值有什么规则?
  16. 什么叫短路求值?
  17. 三元运算符的基本语法是什么?
  18. 为什么复杂表达式通常建议主动添加括号?

7.2 代码阅读

禁止运行,预测结果:

public class OperatorRead01 {
    public static void main(String[] args) {
        int a = 5;

        System.out.println("abc" + a);
        System.out.println(a + 5);
        System.out.println("itheima" + a + 'a');
        System.out.println(a + 'a' + "itheima");
    }
}

要求:

  1. 逐行预测结果。
  2. 写出每一个 + 执行时左右操作数的类型。
  3. 解释最后一行为什么首先发生数值运算。

继续分析:

public class OperatorRead02 {
    public static void main(String[] args) {
        int a = 10;
        int x = ++a;

        int b = 10;
        int y = b++;

        System.out.println(a + "," + x);
        System.out.println(b + "," + y);
    }
}

要求:

  1. 不运行代码预测结果。
  2. 用“先加后用”和“先用后加”解释整个过程。

继续分析:

public class OperatorRead03 {
    public static void main(String[] args) {
        int number = 0;

        boolean a = false && (++number > 0);
        boolean b = true || (++number > 0);

        System.out.println(a);
        System.out.println(b);
        System.out.println(number);
    }
}

要求解释:

  1. 哪些右侧表达式没有执行?
  2. 为什么?
  3. number 最终是多少?

7.3 手写代码

  1. 分别使用 + - * / % 对两个整数完成运算。
  2. 分别使用两种方式让 5 / 2 得到 2.5
  3. 模拟点赞数量连续增加三次,再取消一次点赞。
  4. 使用 % 判断一个整数是奇数还是偶数。
  5. 使用三元运算符找出两个整数的最大值。
  6. 判断某手机价格是否处于 4000~6000 元之间。
  7. 使用逻辑运算符实现“年龄处于 18~60 岁之间”的判断。
  8. 自己设计代码证明 &&& 在执行右侧表达式方面存在区别。

7.4 Debug

下面代码存在问题:

public class OperatorDebug01 {
    public static void main(String[] args) {
        int a = 5;

        double result = a / 2;

        System.out.println(result);
    }
}

需求:

  1. 判断为什么结果没有得到预期的 2.5
  2. 问题发生在赋值阶段还是除法阶段?
  3. 给出至少两种合理修复方式。

继续分析:

public class OperatorDebug02 {
    public static void main(String[] args) {
        byte value = 10;

        value = value + 20;

        System.out.println(value);
    }
}

要求:

  1. 判断为什么不能直接编译。
  2. 结合上一章类型提升解释原因。
  3. 分别使用合理的普通赋值和复合赋值完成修复。
  4. 比较两种方案的类型转换含义。

7.5 综合训练

设计一个简单的“商品筛选判断器”。

固定以下数据:

商品价格:5299
用户最低预算:4000
用户最高预算:6000
库存:10
会员状态:true

程序至少需要判断:

价格是否符合预算
商品是否有库存
用户是否为会员
是否同时满足购买条件

要求:

  1. 至少使用关系运算符。
  2. 至少使用 &&||
  3. 至少使用一次 ! 或三元运算符。
  4. 每一个复杂条件都使用有语义的 boolean 变量保存。
  5. 不允许把所有逻辑强行塞进一条超长表达式。

7.6 本章验收

关闭资料后确认:

  • [ ] 能解释 5 / 25.0 / 2 的区别。
  • [ ] 能熟练使用 %
  • [ ] 能独立分析连续字符串 + 的执行结果。
  • [ ] 能解释 a++++a 的区别。
  • [ ] 能区分 ===
  • [ ] 能解释复合赋值中的隐式类型转换。
  • [ ] 能熟练使用六种关系运算符。
  • [ ] 能写出 & | ^ ! && || 的布尔规则。
  • [ ] 能解释 &&|| 的短路过程。
  • [ ] 能正确使用三元运算符。
  • [ ] 能主动使用括号提高复杂表达式的可读性。

如果你仍然主要依赖“看到符号猜意思”,而不能根据操作数类型、运算顺序和表达式类型分析结果,则本章还没有真正掌握。