目录
计算机的最基本用途之一就是执行数学运算,作为一门计算机语言,Java也提供了一套丰富的运算符来操纵变量。
运算符优先级表
优先级 | 符号 | 名称 | 结合性(与操作数) | 目数 | 说明 |
1 | . | 点 | 从左到右 | 双目 | |
( ) | 圆括号 | 从左到右 |
| ||
[ ] | 方括号 | 从左到右 |
| ||
2 | + | 正号 | 从右到左 | 单目 | |
- | 负号 | 从右到左 | 单目 | ||
++ | 自增 | 从右到左 | 单目 | 前缀增,后缀增 | |
- - | 自减 | 从右到左 | 前缀减,后缀减 | ||
~ | 按位非/取补运算 | 从右到左 | 单目 | ||
! | 逻辑非 | 从右到左 | 单目 | “!”不可以与“=”联用 | |
3 | * | 乘 | 从左到右 | 双目 | |
/ | 除 | 从左到右 | 双目 | 整数除法:取商的整数部分,小数部分去掉,不四舍五入 | |
% | 取余 | 从左到右 | 双目 | ||
4 | + | 加 | 从左到右 | 双目 | |
- | 减 | 从左到右 | 双目 | ||
5 | << | 左移位运算符 | 从左到右 | 双目 | |
>> | 带符号右移位运算符 | 从左到右 | 双目 | ||
>>> | 无符号右移 | 从左到右 | 双目 | ||
6 | < | 小于 | 从左到右 | 双目 | 关系运算符“大于”说明 |
<= | 小于或等于 | 从左到右 | 双目 | ||
> | 大于 | 从左到右 | 双目 | ||
>= | 大于或等于 | 从左到右 | 双目 | ||
instanceof | 确定某对象是否属于指定的类 | 从左到右 | 双目 | ||
7 | == | 等于 | 从左到右 | 双目 | 关系运算符“==”说明 |
!= | 不等于 | 从左到右 | 双目 | ||
8 | & | 按位与 | 从左到右 | 双目 | |
9 | | | 按位或 | 从左到右 | 双目 | |
10 | ^ | 按位异或 | 从左到右 | 双目 | |
11 | && | 短路与 | 从左到右 | 双目 | |
12 | || | 短路或 | 从左到右 | 双目 | |
13 | ? : | 条件运算符 | 从右到左 | 三目 | |
14 | = | 赋值运算符 | 从右到左 | 双目 | |
+= | 混合赋值运算符 |
| |||
-= |
| ||||
*= |
| ||||
/= |
| ||||
%= |
| ||||
&= |
| ||||
|= |
| ||||
^= |
| ||||
<<= |
| ||||
>>= |
| ||||
>>>= |
|
总共有42个,其实开发过程中常用的只有一小部分。因为运算符太多,不一一举例说明,挑选几个有意思的说说。
"&&"和"&"
这两个都是“与”运算符,英语“and”的意思。都可以用作逻辑运算,两边的表达式都为true的时候,整个运算结果才为true,否则为false。两者的区别如下:
- "&&"叫做短路与,是逻辑运算符。只能运算boolean类型的数据,当第一个表达式的值为false的时候,则不再计算第二个表达式,直接返回false。
- "&"叫做按位与,是位运算符。用作逻辑运算时,用它连接的两个表达式都要判断,即使前边的一个结果是false,后边的表达式依旧会执行。本人认为作逻辑运算时,其本质还是按位与,但没有找到有力的证据。
- 短路与比按位与效率高。
举例说明
public class TestMain {
public static void main(String[] args) {
int a = 1;
int b = 1;
System.out.println(a>1 && a++>1);
System.out.println(b>1 & b++>1);
System.out.println(a);
System.out.println(b);
}
}
执行结果如下。可以看出 a 的值没有变化, b 做了自增计算,变为了2。
短路与比较简单,再此不做过多分析。接下来分析按位与的原理。
举例1
System.out.println(5&3);
根据按位与计算规则,不难算出,这段代码的输出结果是 1 。 计算机中的数据都是以二进制存储的,因此需要将两个操作数转换为二进制,计算过程如下:
5 转换为二进制:0000 0000 0000 0000 0000 0000 0000 0101
3 转换为二进制:0000 0000 0000 0000 0000 0000 0000 0011
-------------------------------------------------------------------------------------
1 转换为二进制:0000 0000 0000 0000 0000 0000 0000 0001
举例2
举例3
比较例2和例3发现,这两段代码执行的jvm指令是一样的。boolean类型有两个值,分别是true和false,用二进制表示就是1和0,在存储时占1个字节,运行时用的jvm指令是int类型的指令(先不用纠结这个)。得出结论boolean类型的按位与,其实就是int类型的0或1两个数值按位与,得到的结果再转化为boolean类型。
举例4
public class TestMain {
public static void main(String[] args) {
int a = 1;
boolean bool = a>1 & a<1;
System.out.println(bool);
}
}
执行结果:false
a>1 & a<1 ==> false & false ==> 0 & 0 ==> 0,所以bool = false。
其实"|"和"||"两个运算符实现原理也不同,感兴趣的可以自己写一下,查看他们执行的jvm指令。
"i++"和"++i"
先看以下示例
public class TestMain {
public static void main(String[] args) {
int i = 1;
i = i++;
int j = 1;
j = ++j;
System.out.println(i);
System.out.println(j);
}
}
大家先猜测 i 和 j 的结果,然后执行下代码,查看执行结果是否和自己预测的一致。执行结果:
是不是感到疑惑,两个结果不一样。究竟是什么原因呢?有人给出的解释是:i = i++ 先赋值再自增,而 j = ++j 先自增后赋值。查看运算符优先级表,却发现 ++ 运算符优先级比 = 运算符优先级高。瞬间感到迷茫了,有木有。
接下来,从jvm指令分析原因
i = i++ 代码:
public class TestMain {
public static void main(String[] args) {
int i = 0;
i = i++;
}
}
main方法的jvm字节码指令:
我们一行一行的来分析jvm指令,首先我们要说两个概念,一个是局部变量表,一个是操作栈,main()方法中有两个变量。一个是参数变量args,放在局部变量表索引为0的位置,我们不要用关注它;另另一个就是我们关注的重点对象局部变量 i ,放在局部变量表索引为1的位置,如下图:
“ iconst_0 ” i代表int类型,const代表常量,0就代表整数0,整句话的意思就是把int类型的常量0放入操作栈的栈顶中,图解如下:
“ istore_1 ” i代表int类型,store代表存储,1代表位置为1的变量,整句话的意思就是把操作栈中栈顶的值拿走,保存到位置为1的变量上,图解如下:
“ iload_1 ” i代表int类型,load代表加载变量的值,1代表位置为1的变量,整句话的意思就是把位置为1的变量的值加载到操作栈的栈顶中,图解如下:
“ iinc 1 by 1 ” i代表int类型,inc(increment)代表增加,这里还有两个1,前面的1代表对位置为1的变量,第2个1代表增加1,整句话的意思就是把位置为1的变量的值增加1,图解如下:
注:自增操作不会改变操作栈中的值,所以变量i的值自增后变成了1,而操作栈中的值还是0。
“ istore_1 ” i代表int类型,store代表存储,1代表位置1的变量,整句话的意思就是把栈顶中的值拿走,保存到位置为1的变量中,图解如下:
所以,这几行字节码合起来看,i++不就是先自增,然后才返回自增之前的值嘛!!所以大家千万别搞错顺序了。 用代码理解的话,就相当于下面的代码:
int temp = i;
i = i + 1;
return temp;
i = ++i 代码
public class TestMain {
public static void main(String[] args) {
int i = 0;
i = ++i;
}
}
main方法的jvm字节码指令:
i++ 代码与指令:
++i 代码和指令:
总结:
- i = i++ 与 i = ++i 都是先自增,再赋值。
- i = i++ 的指令集中,先将变量表中 i 的值 0 保存到操作数栈中,再将变量表中 i 的值加 1,最后把操作数栈的值 0 出栈保存到 变量表 i 中。
- i = ++i 的指令集中,先将变量表中 i 的值加 1,再将变量表中 i 的值 1 保存到操作数栈中,最后把操作数栈的值 i 出栈保存到 变量表 i 中。
- 没有赋值操作的 i++ 与 ++i jvm指令集是一样的。
参考资料:
https://blog.csdn.net/android_cai_niao/article/details/106027313
https://www.cnblogs.com/gw811/archive/2012/10/13/2722752.html