JavaSE通关手册:运算符

一、算术运算符

2.1 基本四则运算符:+、-、*、/、%

这五个均为二元运算符,使用时必须具备左右两个操作数,分别对应加、减、乘、除、取模(求余数)运算。

代码示例:

java 复制代码
    public static void main(String[] args) {
        int a = 10;
        int b = 20;

        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);
    }

上述内容过于简单,不在过多赘述。

规则:

1、两个int类型做除法,结果仍然是int类型,会向下取整,直接舍弃小数部分。

**2、**做除法 / 和取模 % 运算时,右操作数不能为 0,否则程序运行时会抛出 ArithmeticException(算术异常),直接终止运行。

**3、取模运算的扩展:%**不仅支持整型取模,也可以对double等浮点类型取模,但浮点取模没有实际业务意义,实际开发几乎只对整型使用取模。

**4、**运算时的类型自动提升:当两个操作数类型不一致时,小范围类型会自动提升为大范围类型,再参与运算,最终结果为大范围类型。

2.2 增量运算符:+=、-=、*=、/=、%=

代码示例:

java 复制代码
int a = 1;

a += 2;  // 完全等价于:a = a + 2; 先相加,再将结果赋值给a
System.out.println(a);  // 输出:3

a -= 1;  // 等价于:a = a - 1;
System.out.println(a);  // 输出:2

a *= 3;  // 等价于:a = a * 3;
System.out.println(a);  // 输出:6

a /= 3;  // 等价于:a = a / 3;
System.out.println(a);  // 输出:2

a %= 3;  // 等价于:a = a % 3;(原文注释误写为%2,以此处为准)
System.out.println(a);  // 输出:2

核心特性:内置强制类型转换

复合赋值运算符会自动对运算结果做强制类型转换,无需手动强转即可编译通过,这是它和普通算术赋值的核心区别。

java 复制代码
double a = 10.4;
int b = 10;

b += a;  
// 编译通过,等价于 b = (int)(b + a);
// 复合赋值会自动将右侧的double运算结果强转为int,再赋值给b

b = b + a;  
// 编译报错:b+a运算后结果为double类型,大范围类型不能直接赋值给小范围的int

2.3 自增 / 自减运算符:++、--

++的作用是给变量的值加 1,--的作用是给变量的值减 1。二者属于单目运算符,只能操作变量,不能操作常量(常量的值不允许被修改)。

根据运算符与变量的位置,分为前置 和后置两种写法,差异仅在混合运算场景下体现。

一句话------与C语言完全相同。


二、关系运算符

共 6 个关系运算符:

相等判断:==(等于)、!=(不等于)

大小判断:<(小于)、>(大于)、<=(小于等于)、>=(大于等于)

规则:

**1、**禁止连写:Java 不支持数学中的连续比较写法,例如3 < a < 5是语法错误。

错误原因:3 < a的结果是 boolean 类型,无法再和整数 5 做大小比较。

正确写法:需要用逻辑运算符连接,写成 a > 3 && a < 5

**2、**适用范围:关系运算符可用于所有数值类型(byte、short、int、long、float、double、char)的比较;== 和 != 还可用于引用类型比较,但默认比较的是内存地址而非内容。

内容与C语言基本一致,不过多赘述。

三、逻辑运算符

逻辑运算符用于连接多个布尔表达式,组合成更复杂的判断条件,运算结果也为 boolean 类型。

核心有三个:&&(逻辑与)、||(逻辑或)、!(逻辑非)。

3.1 逻辑与 &&

1、语法规则: 表达式1 && 表达式2,左右两边的表达式都必须是布尔类型的结果。

2、语义:对应现实中的 "并且",两个条件同时满足,整体才成立。

3、运算规则 :全真才真,一假则假。只有两边都为 true,结果才是 true;只要有一个为 false,结果就是 false。

代码示例:

java 复制代码
int a = 1;
int b = 2;

// 左边a==1为真,右边b==2为真,全真则真,结果:true
System.out.println(a == 1 && b == 2);  

// 左边为真,右边b>100为假,一假则假,结果:false
System.out.println(a == 1 && b > 100); 

// 左边a>100为假,右边为真,一假则假,结果:false
System.out.println(a > 100 && b == 2); 

// 左右两边都为假,全假则假,结果:false
System.out.println(a > 100 && b > 100);

3.2 逻辑或 ||

1、语法规则: 表达式1 || 表达式2,左右两边的表达式都必须是布尔类型的结果。

2、语义:对应现实中的 "或者",两个条件只要满足一个,整体就成立。

3、运算规则 :一真则真,全假才假。只要有一边为 true,结果就是 true;两边都为 false,结果才是 false。

代码示例:

java 复制代码
int a = 1;
int b = 2;

// 左右都为真,一真则真,结果:true
System.out.println(a == 1 || b == 2);  

// 左边为真,右边为假,一真则真,结果:true
System.out.println(a == 1 || b > 100); 

// 左边为假,右边为真,一真则真,结果:true
System.out.println(a > 100 || b == 2); 

// 左右都为假,全假才假,结果:false
System.out.println(a > 100 || b > 100);

3.3 逻辑非 !

1、语法规则 :!表达式 ,属于单目运算符,只需要一个布尔类型的操作数。

2、语义:对布尔结果取反。

3、运算规则 :真变假,假变真。

3.4 短路

简单概括为4点:

1、短路规则:&&(短路与)只要左边结果为假,右边的代码就直接跳过不执行;||(短路或)只要左边结果为真,右边的代码就直接跳过不执行。这个特性能避开除零报错这类异常。

2、&、| 和 &&、|| 的区别:&和 | 也能做逻辑判断,但没有短路效果,不管左边结果如何,右边的代码一定会完整执行,所以可能触发程序异常。

3、实用要点 :逻辑运算符优先级从高到低是 ! > && > || ;实际开发统一用**&&** 和 ||,几乎不用&、| 做逻辑判断;常利用短路特性先判断变量非空,再调用方法,避免空指针报错。


四、位运算符

位运算是直接操作整数二进制比特位的运算,运算效率远高于普通算术运算,常用于底层开发、性能优化、标志位处理等场景。

Java 核心位运算符共 4 个:&(按位与)、|(按位或)、~(按位取反)、^(按位异或);其中~是单目运算符(仅操作一个数),其余均为二元运算符。 计算机中所有数据底层都以二进制(0 和 1 的序列)存储,位运算就是对二进制的每一位逐一进行逻辑计算。

4.1 按位与 &

运算规则: 两个二进制对应位都为 1 时,结果位才是 1;只要有一个是 0,结果位就是 0。

java 复制代码
int a = 10;    // 定义int变量a,十进制值为10
int b = 20;    // 定义int变量b,十进制值为20
System.out.println(a & b); // 对a和b的二进制位逐位执行按位与运算,输出最终结果

最终结果对应十进制的 0

4.2 按位或 |

运算规则: 两个二进制对应位都为 0 时,结果位才是 0;只要有一个是 1,结果位就是 1。

java 复制代码
int a = 10;    // 定义int变量a,十进制值为10
int b = 20;    // 定义int变量b,十进制值为20
System.out.println(a | b); // 对a和b的二进制位逐位执行按位或运算,输出最终结果

最终结果对应十进制的 30

重要补充:& 和 | 的双重身份

1、操作数是整数类型 (byte、short、int、long)时,执行位运算;

2、当操作数是boolean 类型 时,执行逻辑运算(不支持短路,左右两边表达式一定会全部执行)。

4.3 按位取反~

运算规则: 目运算符,对一个数的所有二进制位整体翻转:0 变成 1,1 变成 0,符号位也会一同翻转。

java 复制代码
// 0x 是十六进制数字的固定前缀
// 0xf 表示十六进制的f,对应十进制15,二进制为 1111
int a = 0xf;

// printf 用于格式化输出
// %x 表示以十六进制格式输出结果,\n 是换行符
System.out.printf("%x\n", ~a); 

按位取反会翻转符号位:正数取反后变为负数,负数取反后变为正数。

4.4 按位异或 ^

运算规则: 两个二进制对应位数值相同则结果为 0,数值不同则结果为 1。

java 复制代码
int a = 0x1;  // 十六进制1,对应十进制1,二进制为 0001
int b = 0x2;  // 十六进制2,对应十进制2,二进制为 0010
// 对a和b执行按位异或运算,最终以十六进制格式输出
System.out.printf("%x\n", a ^ b); 

五、移位运算符

移位运算符是对整数的二进制位进行整体平移的运算,共 3 种:<<(左移)、>>(带符号右移)、>>>(无符号右移),均为二元运算符,运算效率高于普通算术乘除,常用于底层性能优化场景。Java 中所有整数都以补码形式存储,移位操作均基于补码二进制执行。

5.1 左移 <<

运算规则: 将整数的二进制位整体向左移动指定位数:最左侧溢出的高位直接丢弃,最右侧空出的低位统一补 0。

java 复制代码
// 0x10 是十六进制数,对应十进制16,二进制为 0001 0000
int a = 0x10;
// 将a的二进制位左移1位,最终结果以十六进制格式打印输出
System.out.printf("%x\n", a << 1);
// 运行结果(十六进制):20,对应十进制32,等价于 16 * 2^1 = 32

数学等价

对于正数且不溢出的场景,左移 N 位等价于原数乘以 2 的 N 次方,硬件执行效率远高于乘法运算。

符号位风险

左移时原符号位会随移位被溢出丢弃;如果左移位数足够多,最高位变为 1,正数会直接变成负数,发生数值溢出。

5.2 带符号右移 >>

也叫算术右移,核心特点是保留原数的正负符号。

运算规则: 将二进制位整体向右移动指定位数:最右侧低位直接丢弃,最左侧空出的高位补充符号位------ 正数补 0,负数补 1。

java 复制代码
// 示例1:正数右移
int a = 0x10;  // 十六进制10,对应十进制16,二进制高位全为0
System.out.printf("%x\n", a >> 1);
// 运行结果(十六进制):8,对应十进制8,等价于 16 / 2^1 = 8

// 示例2:负数右移
// 0xffff0000 是int类型的负数(最高位为1),对应十进制-65536
int b = 0xffff0000;
System.out.printf("%x\n", b >> 1);
// 运行结果(十六进制):ffff8000
// 原因:负数右移时,左侧补符号位1,因此结果依然是负数

数学等价

对于正数,右移 N 位等价于原数除以 2 的 N 次方(向下取整);负数右移后仍保持负数,数值向 0 的方向靠拢。

5.3 无符号右移 >>>

也叫逻辑右移,不区分正负,高位统一补 0。

运算规则: 将二进制位整体向右移动指定位数:最右侧低位直接丢弃,最左侧空出的高位统一补 0,无论原数是正数还是负数。

java 复制代码
// 0xffffffff 是int类型的 -1,二进制所有位全部为1
int a = 0xffffffff;
// 无符号右移1位,左侧补0,结果以十六进制格式打印
System.out.printf("%x\n", a >>> 1);
// 运行结果(十六进制):7fffffff
// 原因:最高位补0后,原负数会直接变成一个很大的正整数

核心区别总结

>>(带符号右移):保留符号,正数补 0、负数补 1,适合有符号数的除法等价运算。

>>>(无符号右移):不保留符号,统一补 0,负数会变为正数,仅用于无符号数值处理场景。


六、条件运算符

语法格式:

java 复制代码
表达式1 ? 表达式2 : 表达式3

执行逻辑

1、表达式 1 必须是boolean类型的结果(条件判断)。

2、当表达式 1 的值为true时,整个三目表达式的最终结果为表达式 2的值。

3、当表达式 1 的值为false时,整个三目表达式的最终结果为表达式 3的值。

java 复制代码
// 需求:求两个整数a和b中的较大值
int a = 10;       // 定义整型变量a,初始值10
int b = 20;       // 定义整型变量b,初始值20
// 三目运算:判断a > b是否成立
// 成立则取a的值,不成立则取b的值,最终将结果赋值给max
int max = a > b ? a : b;
// 本例中a>b为false,因此max被赋值为b的值20

与C语言基本一致,不过多赘述。


七、运算符的优先级

在复合表达式中,运算按运算符优先级从高到低执行,而非严格从左到右。

易错示例:移位运算符的优先级陷阱

代码示例(错误写法,结果不符合预期)

java 复制代码
// 需求:计算a和b的平均值,预期逻辑为 a + (b - a) / 2
int a = 10;               // 定义整型变量a,初始值为10
int b = 20;               // 定义整型变量b,初始值为20
int c = a + (b - a) >> 1; 
/*
 * 预期执行顺序(开发者的主观想法):
 * 1. 先算括号内:b - a = 10
 * 2. 再右移1位(等价于整数除以2):10 >> 1 = 5
 * 3. 最后相加:a + 5 = 15
 * 
 * 实际执行顺序(Java优先级规则):
 * 算术运算符 + 的优先级 高于 移位运算符 >>
 * 1. 先算括号内:b - a = 10
 * 2. 再执行加法:a + 10 = 20
 * 3. 最后执行右移:20 >> 1 = 10
 * 最终结果为10,和预期的15完全不符
 */
System.out.println(c); // 运行输出:10

最佳实践与正确写法

运算符优先级无需死记硬背,遇到含多种运算符、易产生歧义的代码,主动加括号明确运算顺序是最稳妥的写法;括号优先级最高,可强制控制运算顺序,彻底规避优先级带来的逻辑错误。

java 复制代码
// 需求:计算a和b的平均值
int a = 10;                // 定义整型变量a,初始值为10
int b = 20;                // 定义整型变量b,初始值为20
int c = a + ((b - a) >> 1);
/*
 * 通过括号明确指定执行顺序,完全规避优先级问题:
 * 1. 最内层括号:b - a = 10
 * 2. 第二层括号:10 >> 1 = 5(正整数右移1位,等价于整数除法除以2)
 * 3. 最后执行加法:10 + 5 = 15
 * 结果完全符合平均值的预期逻辑
 */
System.out.println(c); // 运行输出:15

移位运算符补充说明

对于正整数,>> 1 的效果和整数除法 /2 完全一致,是底层优化的常见写法;但正是因为其优先级低于加减,混合运算时必须搭配括号使用。

工程编码规范

实际开发中永远不要依赖运算符优先级书写复杂表达式。哪怕优先级顺序是确定的,添加括号也能大幅提升代码可读性,降低后续维护的理解成本和出错概率。


学无止境,代码不息。今天的分享就到这儿,有错误或者更好的思路,欢迎大佬们指正交流 🤝📚

相关推荐
jyOverQ1 小时前
从零学 JVM(七):垃圾收集器详解——Serial、ParNew、Parallel、CMS 与 G1
java·jvm
liangbo71 小时前
Spring系列11:切点表达式
java·spring
3095566662 小时前
股票接口返回 404 与空数组的排查笔记:证书状态与级别参数
java·笔记·python·ios·iphone·股票数据api·股票数据接口
nice_lcj5202 小时前
MySQL 存储引擎入门:InnoDB 和 MyISAM 到底怎么选
java·数据库·mysql·面试·innodb·存储引擎·myisam
文丑颜不良啊2 小时前
LeetCode2861~2880题目解析
java·leetcode
装不满的克莱因瓶2 小时前
设计模式之策略模式核心概念和应用场景
java·后端·设计模式·架构·系统架构·策略模式
晴天的雨.9922 小时前
【C++】继承篇
java·开发语言·c++
SomeOtherTime2 小时前
第 1 讲:内存布局与所有权解剖——栈帧、胖指针与“移动/拷贝”在汇编层面的真实面目
java·开发语言·汇编
qq_406981032 小时前
c++ lamda(三)Lambda表达式
java·前端·c++