Java基础篇——运算符

"本文详细介绍了Java运算符的优先级表,特别讨论了逻辑运算符"&&"与"&"的区别,以及自增运算符"i++"和"++i"的使用细节。通过实例分析了JVM字节码,揭示了它们在实际运算中的行为差异。文章适合初级到中级Java开发者阅读,以加深对运算符理解。"
摘要由CSDN通过智能技术生成

目录

运算符优先级表

"&&"和"&"

"i++"和"++i" 


计算机的最基本用途之一就是执行数学运算,作为一门计算机语言,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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值