不一样的Java运算符

1. 运算符按照功能进行分类

1.1算术运算符

       1.+(加)    2.-(减)    3.*(乘)     4./(除)     5.%(取余或称为取模)     6.++(自增)    7. --(自减)

1.2赋值运算符

       1.=(赋值)    2.+=(加等)     3.-=(减等)     4.*=(乘等)     5./=(除等)     6.%=(取模等)  

1.3关系运算符(或称为比较运算符)

      1.>(大于)     2.>=(大于等于)     3.<(小于)     4.<=(小于等于)   5. !=(不等于)   6.==(等于)

1.4逻辑运算符

     1.& (逻辑与)      2.| (逻辑或)    3.^ (逻辑异或)    4.! (逻辑非)    5.&& (短路与)   6.|| (短路或)     

1.5位运算符

     1. &(按位与)     2.|(按位或)      3.^(按位异或)     4.~(按位取反)   5.<<(按位左位移)   6.>>(按位右位移)   7.>>>(无符号的按位右位移)

运算符的思维导图
在这里插入图片描述

2.算术运算符详解


2.1除(/)和取模(%)

两者的区别:
若进行一个除法运算,使用除(/)得到的结果是商的值,而用取模(%)操作,得到的是余数的值。例如:

 int x = 7;
 int y = 2;
 int result1 = x/y;
 int result2 = x%y;

由于7除以2,商为3,余数为1。因此result1结果为3,result2的结果为1。
原理:由于7和2都是int类型,两个int类型的值之间进行运算结果仍为int类型,因此上面7除以2的结果为3,而不是3.5。对于取模(%)运算,它专门负责接收余数。

2.2自增运算符(++)

说明:这个运算符是重点也是难点。下面列举一些关于自增运算符的知识点,自减运算符(–)与此类似。

2.2.1 使用场合1

int x = 1;
x = x+1;  //x结果为2
    x++;   //x结果为2
    ++x;   //x结果为2

这三个操作方式的结果都是2,都是将x的值进行加1操作。x++和++x单独使用时,两者作用相同,都是自增1。

2.2.2 使用场合2

int x = 1;
int y = x++;    //x的结果为2,y的结果为1
int y = ++x;    //x的结果为2,y的结果为2

这两个操作方式的x结果都为2,但y的结果却不同,原因是进行了赋值操作。 x++和++x在赋值操作中,作用不同。

原理:x在想要做值交换(赋值操作)时,会产生一个临时的副本空间(备份),最后是将副本空间(备份)中的值进行赋值,而不是本身的值。由于++在变量的前面(如++x),表示先自增后备份;++在变量的后面(如x++),表示先备份后自增。
下图是int y=++x在内存中的实现机理:
第1步:x先自增1
第2步:将自增后的值放入副本空间(原空间中还有x值)
第3步:将副本空间中的值赋值给y
第4步:赋值结束后,副本空间被销毁
因此在进行int y=++x;操作时,x的结果为2,y的结果也为2。
在这里插入图片描述
下图是int y=x++在内存中的实现机理:
第1步:x先存入临时空间进行备份(原空间中还有x值)
第2步:x自增1
第3步:将副本空间中的值赋值给y
第4步:赋值结束后,副本空间被销毁
因此在进行int y=x++;操作时,x的结果为2,y的结果为1。
在这里插入图片描述

2.2.3 使用场合3

 int a = 1;
 a = a++;    //a的结果为1
 a = ++a;   //a的结果为2

原理和上面类似,只要记住++在变量的前面(如++x),表示先自增后备份;++在变量的后面(如x++),表示先备份后自增最后都是将备份里面的值赋值给左边变量。
以 int a = a++ 为例:
下图是int a=a++在内存中的实现机理:
第1步:a先存入临时空间进行备份
第2步:a自增1
第3步:将副本空间中的值再赋值给a
第4步:赋值结束后,副本空间被销毁
因此在进行int a=a++;操作时,a的结果为1。
在这里插入图片描述
附赠一道题:

 int a = 0;
 for(int i=1; i<=100; i++) {
  a = a++;
  }     
 System.out.println("a为"+a);//结果仍为1

上面输出a的结果为1;若将循环条件改为a=++a;则a的结果为100。
若将上面程序稍微改动。

 int a = 0;
 int b = 0;
 for(int i=1; i<=100; i++){
   b = a++;
 }
 System.out.println("b为"+b);//结果为99

上面输出b的结果为99;若将循环条件改为b=++a;则b的结果为100。
这些原理和上面讲的相同,读者们对比这几个例子再详细理解下。

2.2.4 使用场合4

一道笔试题

 int m = 1;   //第一次变化为:2  第二次变化为:1  第三次变化为:0   最终结果为0(按最后变化的为准)
 int n = 2;   //第一次变化为:3  第二次变化为:2  第三次变化为:1  最终结果为1
 int sum = m++ + ++n - n-- - --m + n-- - --m;
            1  +  3  -  3  -   1  + 2  -   0    //结果为2
 System.out.println("m为"+m);    //m为0
 System.out.println("n为"+n);     // n为1
 System.out.println("sum为"+sum);    //sum为2

解释:int sum = m++ + ++n - n-- - --m + n-- - --m; 首先这是自增(自减)操作与赋值操作的结合,上面我们已经提过,不管有没有赋值操作,x++与++x本身的x值都是增1的,区别在于向左边的变量赋值时x++是先备份后自增,因此左边的变量是x未自增的值,++x是先自增后备份,因此左边的变量是x自增后的值。
该式子右边第一步:m++,则m的值增1,m=2; 向左边赋值是1(m++先备份后自增)。因此 m=2 ;m++为1
该式子右边第二步:++n,则n的值增1,n=3;向左边赋值是3(++n先自增后备份)。因此n=3 ;++n为3
该式子右边第三步:n–,则n的值减1,第二步中n为3,减1后,n为2;向左边赋值是3(没有自减之前的值)。因此n=2;n–为3
该式子右边第四步:–m,则m的值减1,第一步中m的值为2,减1后,m的值为1;向左边赋值是1。因此m=1;–m为1
该式子右边第五步:n–,则n的值减1,第三步中n为2,减1后,n为1;向左边赋值是2。因此n=1;n–为2
该式子右边第六步:–m,则m的值减1,第四步中m为1,减1后,m为0;向左边赋值是0。因此m=0;–m为0。

3.赋值运算符详解


3.1x=x+n和x++的区别

说明:上面式子中的n值得是常量。
初始x为1,如果要让x增加为10。若用x++,则需要执行十次,当然可以用循环;但如果用x=x+10;只需一步即可。
使用x++方式:

 int x = 1;
 for(int i = 1; i<=10; i++){
         x++;
 }

使用x=x+n方式:

 int x = 1;
 int x = x+10;

3.2 x=x+1、x+=1和x++的不同

从执行效率上讲,x=x+1 < x+=1 < x++
三者执行的结果都是使x加1。但在某些时候执行时会有所不同。如:

int x = 1;
x+=1;     //x为2
x++;      //x为2
x=x+1;   //x为2

以上三种情况不会出现问题,结果都是2。接着该一个条件,将x的类型该为byte,再来看:

byte x = 1;
x+=1;     //x为2
x++;      //x为2
x=x+1;   //报错,需要类型转换,需改为x=(byte)(x+1);

此时,x=x+1就会报错,是由于x+1得到的是一个int型值,如果赋值给byte型的x,需要强制类型转换。也许有人知道这个原因,但为什么x+1得到的是一个int型的值呢?其他两种情况得到的就是byte型的值呢?原因在于它们的执行机理不同。x=x+1,是将右边x的值(byte类型)与常量区中的值(1)进行相加,然后再赋值。问题在这,常量区的常量值默认都是int类型,因此x的值是byte类型的1,与常量int类型的值1相加,结果为int类型的值2。虽然int类型和byte类型可以相加,但不可以赋值,byte可以向int赋值(小空间可以向大空间赋值),但int不能向byte赋值(大空间不能向小空间赋值,会造成损失)。因此这里就必须使用强制类型转换操作。

知识延伸:(可以选择不阅读此段文字)上面所说,常量区中的值为int(32bit位)类型,因此可以看源代码,第一句:byte x=1;赋值操作符后面是一个常量。1为int类型32位,将1赋值给byte类型的x,此时赋值操作会进行自动的转换,将int类型后多余的位去掉,转换为byte类型的1再存入x中。第二句:x+=1;赋值操作符后面是一个常量。这里是byte类型的x先进行与常量(int类型)相加,这时加号(+)自动的做了类型提升,得到了int类型的值2,然后进行赋值,赋值(=)与加号(+)在一起,属于一个运算符,因此这里的=号自动的将int类型的2转化了byte类型的2。但x=x+1不同,这里的=和+是分开的,两者各做各的,赋值(=)后面是个表达式,不能进行自动转化。总结:若赋值操作符(包含复合的赋值操作符,如+=、-=等)后面是一个常量,则可以进行自动的类型转换,若赋值操作符后面是一个表达式,则不能进行自动的类型转换,就如上题所出现的情况

4.关系运算符详解


4.1"=“与”=="的区别

”=“一个等号:是一个赋值操作符,它的作用是将等号右边的结果(结果可以为值或引用)赋值给等号左边的变量空间中。
“==”两个等号:是一个关系运算符,它的作用是比较等号左边和右边的元素(可以为值或引用)是否一致。

4.2关系运算的最终结果是什么?

使用关系(比较)运算时,进行运算的结果是一个boolean类型的值(true或false)。如:3>2的结果为true,5<4的结果为false,3==2的结果为false,3!=2的结果为true 等等。

5.逻辑运算符详解

注意观察,上面的关系运算符可以这样认为:关系运算都是在判定一个条件的真假(如3>2、5<4),当判定两个及以上条件的真假时,需用到逻辑运算。逻辑运算符前后两个表达式的结果都为boolean类型。

5.1逻辑与(&)、逻辑或(|)、逻辑异或(^)、逻辑非(!)

  1. 逻辑与(&):它表示并且的意思,只有当&前后两个条件都满足的时候(即两个条件的结果都是true),最终才为true;只要有一个条件不满足,则最终结果为false。eg:(4>2) & (3>1)的结果为true;(4>5) & (4>2)的结果为false。
  2. 逻辑或(|):它表示或者的意思,只要|前后两个条件满足一个,结果就为true;只有当两个条件都不满足时,结果才为false。如:(3>2) | (3>6)的结果为true;(3>4) | (3>5)的结果为false。
  3. 逻辑异或(^):它指的是只有当前后两个条件不一样时,它的结果才会为true;若前后两个条件的结果相同,则是false。如:(3>2) ^ (3>4)的结果为true;(3>2) ^ (3>1)的结果为false。
  4. 逻辑非(!):它是将条件的结果取反。如:!(3>2)的结果为false;!(4>5)的结果为true。

5.2逻辑与(&)与短路与(&&)的区别

首先看一个例子:(3>5) & (3>2) 与 (3>2) && (3>5) 两者的运算结果都是false。都为什么会有短路与(&&)的存在呢?
逻辑与(&)的执行过程:先判断3>5,结果为false,再判断3>2,结果为true,最后false和true相与(&),最后结果为false。
短路与(&&)的执行过程:先判断3>5,结果为false,不再进行判断,最后结果为false。

看到这里,大家也许对两者有所了解,因为不论是逻辑与(&)还是短路与(&&),本质都是进行与运算。因此当第一个条件为false时,不论后面的条件结果是false还是true,最终结果一定是false;短路与(&&)就是这样做的,但逻辑与(&)是将所有条件都计算出来再做最后的结果。因此,短路与(&&)的执行执行效率比逻辑与(&)要强一些(减少了运算)。
但是短路与(&&)的执行效率是不是总是比逻辑与(&)要强一些呢?答案是否定的。
短路与(&&)只有像上面例子的情况下才会发生短路(只执行一个条件就得出结果),若将上面例子稍微掉个顺序:(3>2) && (3>5)这种情况下,第一个条件的结果是true,使用短路与的情况下,也无法判断最终的结果是false还是true,因为第二个条件的结果未知,因此,在这种情况下,短路与和逻辑与的执行过程是一样的,都要将两个条件的结果计算出来,在进行逻辑运算。

总结:
1.不论使用逻辑与(&)还是短路与(&&),都不会影响最终的结果
2. 短路与(&&)在正常情况下与逻辑与(&)的执行过程是一致的,只有当第一个条件为false的时候,才会发生短路,提高效率,其余情况下并不会提高效率,和逻辑与(&)一样。

5.2逻辑或(|)与短路或(||)的区别

上面已经详细讲解了逻辑与(&)和短路与(&&)的区别,因此逻辑或(|)与短路或(||)与此类似,只需将第一个条件是true的情况下,才会发生短路(不再进行第二个条件的判断,最终结果直接为true)。
总结:
1.不论使用逻辑或(|)还是短路或(||),都不会影响最终的结果
2. 短路或(||)在正常情况下与逻辑或(|)的执行过程是一致的,只有当第一个条件为true的时候,才会发生短路,提高效率,其余情况下并不会提高效率,和逻辑或(|)一样。

6.位运算符详解


6.1 按位运算和逻辑运算的主要不同

对于小白来说,看到这里,也许会有些疑惑,这里与(&)、或(|)、异或(^)不是在上面逻辑运算时出现过了吗,其实,它们的符号虽然相同,但表达的意思却完全不同。

  1. 逻辑运算符的前后是表达式,按位运算符的前后是具体数值。
  2. 逻辑运算的结果是一个boolean类型的值,按位运算的结果是一个数值。

6.2 按位与(&)、按位或(|)、按位异或(^)、按位取反(~)

  1. 按位与(&):eg:3 & 5;它的运算过程:先将3转换为二进制数,将5转换为二进制数,然后让每一位二进制数进行相与(&)。相与的原理(0 & 0=0,0 & 1=0,1 & 0=0,1 & 1=1)。

       过程为:     3的二进制数:00000011
                   5的二进制数:00000101
                     进行相与: 00000001   结果为1。
    
  2. 按位或(|):eg:3 | 5;它的运算过程,首先将两个数转换为二进制数,然后进行逐位相或。相或的原理(0 | 0=0,0 | 1=1,1 | 0=1,1 | 1=1)。

       过程为:3的二进制数: 00000011
              5的二进制数: 00000101
                 进行相与: 00000111   结果为7。
    
  3. 按位异或(^):eg:3 ^ 5;它的运算过程,首先将两个数转换为二进制数,然后进行逐位异或。异或的原理(0 ^ 0=0,0 ^ 1=1,1 ^ 0=1,1 ^ 1=0)。

      过程为:3的二进制数: 00000011
              5的二进制数:00000101
                 进行相与:00000110   结果为6。
    
  4. 按位取反:eg:~3;它的运算过程,首先将两个数转换为二进制数,然后进行逐位取反。异或的原理(1转换为0,0转换为1)。

      过程为: 3的二进制数: 00000011
              ~3的二进制数:11111100      //这是源码
               
       将~3的二进制数取反码:10000011    //反码
          将反码加1得到补码:10000100    //补码     结果为-4
    

在这里,对3进行求反后,需要再进行转换,因为计算机中存的都是补码,3求反后是负数,负数的补码是先求反码再加1,得到的数才是真正的~3。因此,对3按位取反的值为-4。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值