Day5|Java运算符与表达式详解

lazysnail2025/07/10Java基础

运算符是Java程序中最基础的逻辑单元,直接影响代码的正确性和效率。

无论是简单的数学计算,还是复杂的条件判断,都离不开对运算符的精准掌握。

无论你是编程新手还是希望巩固基础,本文将通过代码示例和原理拆解,助你彻底掌握运算符的“明规则”与“潜规则”。

一、Java 运算符全景图

1. 运算符分类与核心用途

类别运算符示例核心用途行为特点
数值类+ - * / % ++ --数学计算类型自动提升、整除舍入
比较类> < >= <= == !=条件判断返回 boolean 值
逻辑类&& || !布尔逻辑组合短路求值(&&、||)
位操作类& | ^ ~ << >> >>>二进制位操作高效底层控制
赋值类= += -= *= /= %=变量赋值与复合运算隐含类型转换风险
条件类?:简化条件逻辑唯一的三目运算符

二、数值运算符

1. 基础算术运算

基础算术运算比较好理解,无非就是加减乘除,再就是取模,自增自减,注意一些细节即可。

int a = 10, b = 3;  
System.out.println(a / b);   // 3(整型除法舍去小数)  
System.out.println(a % b);   // 1(取余)  
System.out.println(5 / 2.0); // 2.5(浮点参与保留小数)  

TIP

整数除法直接截断小数,不四舍五入!

2. 自增/自减运算符

前置与后置的本质区别:

a++:先返回原值,再自增(对应JVM的iload + iinc指令)

++a:先自增,再返回新值

int a = 5;  
int b = a++;  // b=5, a=6  
int c = ++a;  // a=7, c=7  

Thread类的sleep方法中有"a++"的应用:

img

ArrayListSpliterator类(定义在ArrayList中)的forEachRemaining方法有"++a**"的应用:**

img

小练一下:

int x = 3;  
System.out.println(x++ + ++x);  // 结果是什么?

按步分解:

int x = 3;  
// 步骤分解:  
// 1. x++ → 返回3,x=4  
// 2. ++x → x=5,返回5  
// 3. 3 + 5 = 8  
System.out.println(x++ + ++x); // 输出8  

TIP

这样的表达式在实际开发中几乎不可能使用!因为可读性极差。之所以给出这种案例,只是为了理解前置/后置自增。

三、比较与逻辑运算符

1. 比较运算符

运算符描述示例(假设 a=5, b=10)结果
==等于a == bfalse
!=不等于a != btrue
>大于a > bfalse
<小于a < btrue
>=大于等于a >= 5true
<=小于等于b <= 10true
int a = 5;
int b = 10;
// 比较运算符
System.out.println("a > b: " + (a > b)); // false
System.out.println("a < b: " + (a < b)); // true
System.out.println("a >= b: " + (a >= b)); // false
System.out.println("a <= b: " + (a <= b)); // true
System.out.println("a == b: " + (a == b)); // false
System.out.println("a != b: " + (a != b)); // true

TIP

所有比较运算符返回boolean值,不可直接用于赋值(如if(1)非法)

2. 逻辑运算符

运算符行为示例
&&左边结果为 false ,右侧逻辑不执行false && (x/0==0) 无异常
&始终执行两侧false & (x/0==0) 抛出异常
||左边为 true ,右侧不执行true || (x/0==0) 无异常
|始终执行两侧true | (x/0==0) 抛出异常

"&&"的短路应用:

// ArrayList
public void ensureCapacity(int minCapacity) {
    if (minCapacity > elementData.length
        && !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
             && minCapacity <= DEFAULT_CAPACITY)) {
        modCount++;
        grow(minCapacity);
    }
}

如果minCapacity > elementData.length条件不成立,那么右侧的就没有执行的必要了。

"||"的短路应用:

// ArrayList
private Object[] grow(int minCapacity) {
    int oldCapacity = elementData.length;
    if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        int newCapacity = ArraysSupport.newLength(oldCapacity,
                minCapacity - oldCapacity, /* minimum growth */
                oldCapacity >> 1           /* preferred growth */);
        return elementData = Arrays.copyOf(elementData, newCapacity);
    } else {
        return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
    }
}

如果oldCapacity > 0的条件成立,那么右侧的逻辑也就没有执行的必要了。

实际开发场景:

if (list != null && !list.isEmpty()) { ... }  

避免空指针:先判空再调用方法。

"&"和"|"是非短路的逻辑运算符:

&(逻辑与):两个操作数均为true时结果为true,但强制计算左右两侧表达式(没有短路特性)。

|(逻辑或):至少一个操作数为true时结果为true,同样强制计算两侧表达式(没有短路特性)。

boolean a = false;
boolean b = true;

// 逻辑与:无论 a 是否为 false,右侧表达式都会被计算
boolean result1 = a & (b || someMethod()); // 右侧的 `someMethod()` 会被调用

// 逻辑或:无论 a 是否为 true,右侧表达式都会被计算
boolean result2 = a | (b && someMethod()); // 右侧的 `someMethod()` 会被调用

四、位运算符

1. 基础位操作

运算符示例二进制操作结果
&5 & 3 (0101 & 0011)按位与0001 (1)
|5 | 3按位或0111 (7)
^5 ^ 3异或(相同为0,不同为1)0110 (6)
~~5取反(包括符号位)11111010 (-6)

TIP

"&"和“|”怎么又出现了? 当操作数是布尔值(boolean)时,& 和 | 是 非短路的逻辑运算符。 当操作数是整数类型(int, long, byte, short, char)时,& 和 | 是 位运算符

按位与(&)

int a = 5;
int b = 3;
System.out.println(a & b); // 输出结果1
// 5 & 3     0001  转换成十进制就是1

img

按位或(|)

int a = 5;
int b = 3;
System.out.println(a | b); // 输出结果7
// 5 | 3     0111  转换成十进制就是7

img

异或(^)

int a = 5;
int b = 3;
System.out.println(a ^ b); //输出结果6
// 5 ^ 3     0110  转换成十进制就是6

img

取反(~)

int a = 5;
System.out.println(~ a);// -6
// ~5     1010  转换成十进制就是6

img

TIP

为什么实际运行结果是-6,但是按照我们推理结果却是6?

int类型的5实际的32位二进制是00000000 00000000 00000000 00000101(之前我们只使用4位是因为前面都是0就没列出来)。

将00000000 00000000 00000000 00000101按位取反的结果是:11111111 11111111 11111111 11111010

最高位变成了1,表示这是一个负数。

剩下的31位数值位表示的是绝对值的补码。

补码转原码的逻辑:取反后加1。

补码: 11111111 11111111 11111111 11111010  
取反: 00000000 00000000 00000000 00000101  
加1:  00000000 00000000 00000000 00000110(十进制6)

符号位是1为负数,数值部分是6,所以结果是-6。

2. 位移运算

在Java中,位移运算符用于对整数类型(如 int、long)的二进制位进行移动操作。

运算符方向填充位规则适用场景
<<左移右侧补 0快速乘以 2 的幂,位掩码操作
>>右移左侧补 符号位处理有符号整数的除法
>>>右移左侧补 0无符号整数处理或强制转正数

左移运算符(<<)

将二进制位向左移动指定位数,右侧空位补0。

int a = 5;       // 二进制: 0000 01012
int b = a << 2;  // 左移2位 → 0001 0100(十进制 20)

等效于乘以 2n(a << n ≈ a * 2^n)。

可能导致符号位变化(如正数变负数)。

带符号右移运算符(>>)

将二进制位向右移动指定位数,左侧空位补 符号位(正数补 0,负数补 1)。

int a = 20;      // 二进制: 0001 0100
int b = a >> 2;  // 右移2位 → 0000 0101(十进制 5)

int c = -20;     // 二进制补码: 1111 1111 1111 1111 1111 1111 1110 1100
int d = c >> 2;  // 右移2位 → 1111 1111 1111 1111 1111 1111 1111 1011(十进制 -5)

等效于整数除法(向下取整)(a >> n ≈ a / 2^n)。

保留符号位,适合处理有符号整数。

无符号右移运算符(>>>)

将二进制位向右移动指定位数,左侧空位补 0(无论正负)。

int a = 20;       // 二进制: 0001 0100
int b = a >>> 2;  // 无符号右移2位 → 0000 0101(十进制 5)

int c = -20;      // 二进制补码: 1111 1111 1111 1111 1111 1111 1110 1100
int d = c >>> 2;  // 无符号右移2位 → 0011 1111 1111 1111 1111 1111 1111 1011(结果为正数)

仅适用于 int 和 long 类型。

强制左侧补 0,可能导致负数变为正数。

位移应用:

// ArrayList
private static long[] nBits(int n) {
    return new long[((n - 1) >> 6) + 1];
}
private static void setBit(long[] bits, int i) {
    bits[i >> 6] |= 1L << i;
}
private static boolean isClear(long[] bits, int i) {
    return (bits[i >> 6] & (1L << i)) == 0;
}

五、赋值与条件运算符

1. 基本赋值运算符(=)

把右侧的值或表达式结果赋给左侧变量。

int a = 10;     // 将 10 赋值给 a
String s = "Hello";

2.算术运算复合赋值符

运算符等价表达式示例说明
+=a = a + ba += 5 → a = a + 5加法赋值
-=a = a - ba -= 3 → a = a - 3减法赋值
*=a = a * ba *= 2 → a = a * 2乘法赋值
/=a = a / ba /= 4 → a = a / 4除法赋值
%=a = a % ba %= 3 → a = a % 3取模赋值

3.位运算复合赋值符

运算符等价表达式示例说明
&=a = a & ba &= 0xFF按位与赋值
| =a = a | b
^=a = a ^ ba ^= mask按位异或赋值
<<=a = a << ba <<= 2 → 左移两位左移赋值
>>=a = a >> ba >>= 1 → 带符号右移带符号右移赋值
>>>=a = a >>> ba >>>= 3 → 无符号右移无符号右移赋值

4. 三目运算符

三目运算符是一种简洁的条件表达式,用于替代简单的 if-else 逻辑。

// 条件 ? 表达式1 : 表达式2
// 如果 条件 为 true,返回 表达式1 的值。
// 如果 条件 为 false,返回 表达式2 的值。
int score = 85;  
String result = (score >= 60) ? "及格" : "补考";  

TIP

表达式1与表达式2必须类型兼容!

六、JLS 规范与优先级总表

Java语言规范(JLS)第15章明确定义了运算符的优先级和结合性。以下是根据JLS17整理的优先级顺序表及关键说明:

1. 运算符优先级

优先级运算符类别运算符结合性示例
1后缀表达式expr++, expr--, (), [], .左→右array[i].toString()
2一元表达式+, -, ++expr, --expr, ~, !右→左-a, ++b, !flag
3乘除模*, /, %左→右a * b / c
4加减+, -左→右a + b - c
5移位<<, >>, >>>左→右x << 2
6关系运算符1<, >, <=, >=左→右a >= 10
7关系运算符2instanceof左→右obj instanceof String
8相等运算符==, !=左→右a == b
9按位与&左→右a & b
10按位异或^左→右a ^ b
11按位或|左→右a | b
12逻辑与(短路)&&左→右a && b
13逻辑或(短路)||左→右a || b
14三元运算符?:右→左a > b ? x : y
15赋值类=, +=, -=, *=, /=, %= 等右→左a += b,x = y = z + 1
16逗号运算符,左→右(a = 1, b = 2)

七、随手写了几个运算符问题,试一试吧

  1. Integer i = 128; Integer j = 128; System.out.println(i == j);
  2. byte b = 1; b = b + 1; 会不会报错?为什么?
  3. System.out.println(5 / 2); 和 5 / 2.0 的区别?
  4. int x = 1; x = x++ + ++x; 最终结果?
  5. a = a++; 和 a = ++a; 有什么差异?

结语

编程的本质是逻辑的精确表达,而运算符正是这表达的核心工具。

建议多动手实践文中的代码示例,尝试挑战综合练习题,将知识内化为本能。

唯有在编码中踩过坑、解过惑,才能真正领悟“纸上得来终觉浅,绝知此事要躬行”的真谛。

最后更新 7/10/2025, 11:14:05 AM