Skip to content
第 7 / 250 章后端⏱ 10 分钟阅读

第 7 章:运算符详解

学习目标

  • 掌握算术、关系、逻辑、位、三元运算符
  • 理解运算符优先级
  • 学会用位运算做企业级权限设计
  • 避免运算符的常见坑

一、Java 运算符全景图

二、算术运算符

2.1 基本算术运算

java
int a = 10, b = 3;

System.out.println(a + b);   // 13(加)
System.out.println(a - b);   // 7(减)
System.out.println(a * b);   // 30(乘)
System.out.println(a / b);   // 3(整数除法,截断小数)
System.out.println(a % b);   // 1(取余/取模)

double x = 10.0, y = 3.0;
System.out.println(x / y);   // 3.3333333333333335(浮点除法)

2.2 自增自减

java
int i = 5;
System.out.println(i++);  // 5(先取值后自增)
System.out.println(++i);  // 7(先自增后取值)
System.out.println(i--);  // 7(先取值后自减)
System.out.println(--i);  // 5(先自减后取值)

自增自减的坑

java
int i = 5;
int j = i++ + ++i;  // ❌ 复杂表达式,结果未定义
// 不同 JVM 实现可能给出不同结果

企业级代码中i++ / ++i 单独使用,不要混进复杂表达式。

2.3 整数除法的陷阱

java
int a = 5, b = 2;
System.out.println(a / b);       // 2(不是 2.5)
System.out.println(a / (double) b);  // 2.5(强转才能得到小数)

// 经典场景:计算平均分
int total = 87, count = 10;
double average = total / count;        // ❌ 8.0(错误:整数除法)
double correct = total / (double) count;  // ✅ 8.7

三、关系运算符

关系运算符返回 boolean 值(true / false)。

java
int a = 10, b = 20;

System.out.println(a > b);    // false
System.out.println(a < b);    // true
System.out.println(a >= b);   // false
System.out.println(a <= b);   // false
System.out.println(a == b);   // false(值相等)
System.out.println(a != b);   // true

== 比较包装类的陷阱

java
Integer x = 100, y = 100;
System.out.println(x == y);       // true(缓存池)
Integer m = 200, n = 200;
System.out.println(m == n);       // false(不在缓存池)
System.out.println(m.equals(n));  // true(值相等)

永远用 equals 比较包装类

四、逻辑运算符

4.1 三种逻辑运算符

java
boolean a = true, b = false;

System.out.println(a && b);   // false(逻辑与 AND)
System.out.println(a || b);   // true(逻辑或 OR)
System.out.println(!a);       // false(逻辑非 NOT)
System.out.println(a ^ b);    // true(逻辑异或 XOR)

4.2 短路求值(关键特性)

&&||短路求值特性:

java
// && 短路:左边为 false,右边不执行
int x = 0;
if (x != 0 && 10 / x > 1) {  // x != 0 为 false,10/x 不执行,避免除零
    System.out.println("ok");
}

// || 短路:左边为 true,右边不执行
if (x == 0 || 10 / x > 1) {  // x == 0 为 true,10/x 不执行
    System.out.println("ok");
}

短路求值的实际用途

  1. 避免除零x != 0 && 10/x > 1
  2. 避免空指针obj != null && obj.method()
  3. 性能优化:把昂贵判断放右边

4.3 &| vs &&||

java
// & 和 | 没有短路
int x = 0;
if (x != 0 & 10 / x > 1) {  // ❌ 仍然会执行 10/x,抛 ArithmeticException
    System.out.println("ok");
}
运算符类型短路用途
&&逻辑与布尔逻辑(推荐)
&位与 / 不短路逻辑与位运算 / 不需要短路时
||逻辑或布尔逻辑(推荐)
|位或 / 不短路逻辑或位运算 / 不需要短路时

五、位运算符

位运算符直接操作二进制位,速度极快。

5.1 基本位运算

java
int a = 5;     // 0101
int b = 3;     // 0011

System.out.println(a & b);   // 1(0101 & 0011 = 0001)按位与:两位都是 1 才为 1
System.out.println(a | b);   // 7(0101 | 0011 = 0111)按位或:任一位是 1 即为 1
System.out.println(a ^ b);   // 6(0101 ^ 0011 = 0110)按位异或:不同为 1
System.out.println(~a);      // -6(~00000101 = 11111010)按位取反

5.2 移位运算

java
int a = 8;     // 00001000

System.out.println(a << 1);   // 16(左移 1 位,相当于 × 2)
System.out.println(a << 2);   // 32(左移 2 位,相当于 × 4)
System.out.println(a >> 1);   // 4(右移 1 位,相当于 / 2)
System.out.println(a >> 2);   // 2(右移 2 位,相当于 / 4)

// >>> 无符号右移:高位补 0
int b = -8;
System.out.println(b >> 1);   // -4(保留符号位)
System.out.println(b >>> 1);  // 2147483644(高位补 0,巨大正数)

5.3 实战案例:企业级权限设计

位运算的经典应用是位掩码权限设计

java
/**
 * 权限位掩码
 * 每个权限占一个 bit 位
 */
public class Permission {
    public static final int READ    = 1 << 0;  // 0001 = 1
    public static final int WRITE   = 1 << 1;  // 0010 = 2
    public static final int DELETE  = 1 << 2;  // 0100 = 4
    public static final int ADMIN   = 1 << 3;  // 1000 = 8

    // 用户权限:可以读 + 写
    int userPerm = Permission.READ | Permission.WRITE;  // 0001 | 0010 = 0011 = 3

    // 检查是否有读权限(按位与)
    boolean canRead = (userPerm & Permission.READ) != 0;   // true
    boolean canDelete = (userPerm & Permission.DELETE) != 0; // false

    // 添加权限(按位或)
    userPerm |= Permission.DELETE;  // 现在 userPerm = 0111

    // 移除权限(按位与 + 按位取反)
    userPerm &= ~Permission.DELETE;  // 现在 userPerm = 0011

    // 切换权限(按位异或)
    userPerm ^= Permission.WRITE;    // 现在 userPerm = 0001
}

权限位掩码的图解

这种设计 30 年来一直是 Unix/Linux 文件系统的标准方式(rwxrwxrwx)。

5.4 实战案例:交换两个整数(不用临时变量)

java
int a = 5, b = 10;

// 用异或交换
a = a ^ b;   // a = 5^10
b = a ^ b;   // b = (5^10)^10 = 5
a = a ^ b;   // a = (5^10)^5 = 10
// 结果:a=10, b=5

// 或者用加减法(有溢出风险)
a = a + b;
b = a - b;
a = a - b;

六、赋值运算符

java
int x = 10;
x += 5;    // 等价于 x = x + 5 = 15
x -= 3;    // 等价于 x = x - 3 = 12
x *= 2;    // 等价于 x = x * 2 = 24
x /= 4;    // 等价于 x = x / 4 = 6
x %= 4;    // 等价于 x = x % 4 = 2

// 位赋值
int flags = 0;
flags |= 1;       // 等价于 flags = flags | 1
flags &= ~2;      // 等价于 flags = flags & ~2
flags ^= 4;       // 等价于 flags = flags ^ 4

七、三元运算符

java
int age = 18;
String status = (age >= 18) ? "成年" : "未成年";
System.out.println(status);  // 成年

// 嵌套三元(不推荐,可读性差)
String level = (score >= 90) ? "A" : (score >= 60) ? "B" : "C";

八、字符串拼接运算符

+ 在 Java 中有双重身份:

java
// 数值加法
System.out.println(1 + 2);          // 3

// 字符串拼接
System.out.println("Hello" + "World");  // HelloWorld

// 混合运算:从左到右
System.out.println(1 + 2 + "3");    // 33(先 1+2=3,再 3+"3"="33")
System.out.println("1" + 2 + 3);    // 123(先 "1"+2="12",再 "12"+3="123")

企业级代码:循环中频繁字符串拼接用 StringBuilder,不要用 +

九、instanceof 运算符

java
Object obj = "Hello";

if (obj instanceof String) {  // 类型检查
    String str = (String) obj;  // 类型转换
    System.out.println(str.toUpperCase());
}

// Java 16+ 模式匹配
if (obj instanceof String str) {  // 直接绑定变量
    System.out.println(str.toUpperCase());
}

十、运算符优先级

从高到低(建议用括号避免混淆):

优先级运算符
1() [] .
2++ -- ~ !
3* / %
4+ -
5<< >> >>>
6< <= > >= instanceof
7== !=
8&
9^
10|
11&&
12||
13? :
14= += -= *= ...
java
// 复杂表达式:靠优先级不如靠括号
int result = 2 + 3 * 4;          // 14(先 3*4=12,再 +2)
int clear  = 2 + (3 * 4);        // 14(加括号更清晰)

// ❌ 反例
if (a > 0 && b < 10 || c == 5) {  // 不加括号可读性差
    // ...
}

// ✅ 正例
if ((a > 0 && b < 10) || c == 5) {  // 明确优先级
    // ...
}

十一、完整代码示例

java
/**
 * 运算符综合示例
 *
 * 配套文档:第 7 章 运算符详解
 * 运行方式:javac OperatorDemo.java && java OperatorDemo
 */
public class OperatorDemo {

    // 权限位掩码常量
    public static final int PERM_READ   = 1 << 0;  // 1
    public static final int PERM_WRITE  = 1 << 1;  // 2
    public static final int PERM_DELETE = 1 << 2;  // 4
    public static final int PERM_ADMIN  = 1 << 3;  // 8

    public static void main(String[] args) {
        // ============ 1. 算术运算符 ============
        System.out.println("=== 算术 ===");
        int a = 10, b = 3;
        System.out.println("a + b = " + (a + b));    // 13
        System.out.println("a - b = " + (a - b));    // 7
        System.out.println("a * b = " + (a * b));    // 30
        System.out.println("a / b = " + (a / b));    // 3(整数除法)
        System.out.println("a % b = " + (a % b));    // 1

        // ============ 2. 自增自减 ============
        System.out.println("\n=== 自增自减 ===");
        int i = 5;
        System.out.println("i++ = " + (i++));  // 5,i 变 6
        System.out.println("++i = " + (++i));  // 7,i 变 7
        System.out.println("i-- = " + (i--));  // 7,i 变 6
        System.out.println("--i = " + (--i));  // 5,i 变 5

        // ============ 3. 关系与逻辑 ============
        System.out.println("\n=== 关系与逻辑 ===");
        int age = 20;
        boolean isAdult = age >= 18 && age <= 60;  // 短路与
        boolean isMinor = age < 18 || age > 60;
        System.out.println("是否成年: " + isAdult);
        System.out.println("是否未成年或老年: " + isMinor);

        // ============ 4. 位运算 ============
        System.out.println("\n=== 位运算 ===");
        System.out.println("5 & 3 = " + (5 & 3));   // 1
        System.out.println("5 | 3 = " + (5 | 3));   // 7
        System.out.println("5 ^ 3 = " + (5 ^ 3));   // 6
        System.out.println("8 << 1 = " + (8 << 1)); // 16

        // ============ 5. 实战:位掩码权限 ============
        System.out.println("\n=== 权限系统 ===");
        int userPerm = PERM_READ | PERM_WRITE;  // 用户有读 + 写权限

        System.out.println("有读权限? " + ((userPerm & PERM_READ) != 0));    // true
        System.out.println("有写权限? " + ((userPerm & PERM_WRITE) != 0));   // true
        System.out.println("有删除权限? " + ((userPerm & PERM_DELETE) != 0)); // false

        userPerm |= PERM_DELETE;  // 添加删除权限
        System.out.println("添加删除后: " + Integer.toBinaryString(userPerm));

        userPerm &= ~PERM_WRITE;  // 移除写权限
        System.out.println("移除写后: " + Integer.toBinaryString(userPerm));

        // ============ 6. 三元运算符 ============
        System.out.println("\n=== 三元 ===");
        int score = 85;
        String result = score >= 60 ? "及格" : "不及格";
        System.out.println("成绩: " + result);

        // ============ 7. 字符串拼接陷阱 ============
        System.out.println("\n=== 字符串拼接 ===");
        System.out.println(1 + 2 + "3");  // 33
        System.out.println("1" + 2 + 3);  // 123
    }
}

运行结果

=== 算术 ===
a + b = 13
a - b = 7
a * b = 30
a / b = 3
a % b = 1

=== 自增自减 ===
i++ = 5
++i = 7
i-- = 7
--i = 5

=== 关系与逻辑 ===
是否成年: true
是否未成年或老年: false

=== 位运算 ===
5 & 3 = 1
5 | 3 = 7
5 ^ 3 = 6
8 << 1 = 16

=== 权限系统 ===
有读权限? true
有写权限? true
有删除权限? false
添加删除后: 111
移除写后: 101

=== 三元 ===
成绩: 及格

=== 字符串拼接 ===
33
123

十二、常见面试题

Q1: i++++i 哪个更快?

理论上 ++i 稍快(无需保存旧值),但实际 JVM 优化后没有差别不要为了性能选择,要看语义。

Q2: &&& 的区别?

运算符类型短路
&&布尔逻辑
&位运算 / 不短路布尔

Q3: 为什么位运算更快?

CPU 直接操作寄存器,无方法调用、无类型检查。但可读性差,只在性能敏感场景用(如权限系统、加密算法)。

Q4: ^ 异或有什么妙用?

  1. 交换两数:不用临时变量
  2. 找出唯一出现一次的数(其他数都出现两次)
  3. 简单加密:XOR 加密

十三、本章小结

要点关键
算术运算注意整数除法会截断,强转才能得到小数
关系运算比较包装类用 equals,不要用 ==
逻辑运算&& || 有短路,& | 没有
位运算速度极快,适合权限掩码、加密、底层算法
字符串拼接+ 从左到右运算,循环中用 StringBuilder
优先级复杂表达式用括号,不要背优先级

动手练习

练习 1:基础题

写一个温度转换器:

java
// 摄氏度 → 华氏度:F = C × 9/5 + 32
// 华氏度 → 摄氏度:C = (F - 32) × 5/9
double celsius = 25;
double fahrenheit = ???;
System.out.println(celsius + "°C = " + fahrenheit + "°F");

练习 2:进阶题

用位掩码实现一个文件权限系统:

java
// 权限:READ(1) / WRITE(2) / EXECUTE(4)
// 给用户赋予读 + 执行权限,检查并打印他能做什么

练习 3:挑战题

不用临时变量,用位运算交换两个 int 的值,并解释每一步为什么能交换。

推荐阅读


下一章第 8 章:流程控制 — 分支结构

本站基于 VitePress 构建 · 由 Codebook 团队维护