Saul's blog Saul's blog
首页
后端
分布式
前端
更多
分类
标签
归档
友情链接
关于
GitHub (opens new window)

Saul.J.Wu

立身之本,不在高低。
首页
后端
分布式
前端
更多
分类
标签
归档
友情链接
关于
GitHub (opens new window)
  • Java入门基础

    • 计算机常识

    • Java语言概述

    • 基本语法

      • Java-关键字与保留字
      • Java-标识符(Identifier)
      • Java-变量
      • Java-基本数据类型-整型
      • Java-基本数据类型-浮点型
      • Java-基本数据类型-char字符
      • Java-基本数据类型-boolean布尔类型
      • Java-基本数据类型之间的运算规则
      • 字符串类型 String
      • 进制
      • 运算符
        • 定义
        • 算术运算符
          • 算术运算符概览
          • 除号 /
          • 取模 %
          • 前缀++和后缀++
          • 前缀--和后缀--
          • 练习1
          • 练习2
        • 赋值运算符
          • 演示
          • 案例1
          • 案例2
          • 案例3
          • 案例4
          • 案例5
          • 案例6
          • 案例7
        • 比较运算符
          • 案例1
          • 案例2
        • 逻辑运算符
          • 区分 &与 &&
          • | 与 ||
          • 练习1
          • 练习2
          • 练习3
          • 练习4
          • 面试题
        • 位运算符
          • << 左移
          • & 与运算
          • | 或运算
          • ^ 异或运算
          • ~ 取反运算
          • 练习
        • 三元运算符
          • 格式
        • 运算符的优先级
      • Scanner类
      • 流程控制-分支结构
      • 流程控制-循环结构
    • 数组

    • 面向对象

    • 异常处理

  • Java核心基础

  • 设计模式

  • Web开发

  • SpringBoot

  • 微服务

  • Elasticsearch

  • 运维

  • 后端
  • Java入门基础
  • 基本语法
SaulJWu
2020-12-04

运算符

# 定义

运算符是一种特殊的符号,用以表示数据的运算、赋值和比较等。

  • 算术运算符
  • 赋值运算符
  • 比较运算符(关系运算符)
  • 逻辑运算符
  • 位运算符
  • 三元运算符

其中,位运算符,在实际开发中用得很少。但看系统源码的时候,会有位运算符,所以还是需要去了解。

# 算术运算符

# 算术运算符概览

运算符 运算 范例 结果
+ 正号 +3 3
- 负号 b=4;-b -4
+ 加 5+5 10
- 减 6-4 2
* 乘 3*4 12
/ 除 5/5 1
% 取模(取余) 7%5 2
前缀++ 自增前:先运算再取值 a=2;b=++a a=3;b=3
后缀++ 自增后:先取值后运算 a=2;b=a++; a=3;b=2
前缀— 自减前:先运算后取值 a=2;b=— a; a=1;b=1
后缀— 自减后:先取值后运算 a=2;b=a— a=1;b=2
+ 字符串连接 "He"+"llo" "Hello"

在Java中

# 除号 /

整数12/整数5结果是?

int num1 = 12;
int num2 = 5;
int result1 = num1 / num2; //2
1
2
3

整形会截尾整数,所以结果是2

接下来在上面案例中,再加上

int result2 = num1 / num2 * num2;
1

结果是10。

猜猜下面的结果:

double result3 = num1 / num2
1

结果是2.0。

为什么?

因为先算num1 / num2,得出的结果是2,然后把2赋值给result3

那么不改变num1和num2的情况下,如何能达到精确结果?

double result4 = num1 / (num2 + 0.0); // 2.4
1
double result5 = (double)num1 / num2; // 2.4
1
  • 小结

只要把其中一个转换为double,就可以求得到精确结果。

# 取模 %

取模其实就是取余数,比如7/5=1余2,所以取出2作为结果。开发中,经常使用%来判断能否被除尽的情况。

比如:取模2,就可以判断奇偶数,10%2=0,偶数,11%2=1,奇数。

在取模运算中,除数叫模数,被除数叫被模数。例如10%2=0,这个公式中,10是被模数,2是模数。跟除数和被除数的概念一样。

来看看下面的案例,每个result的结果分别是多少?

int m1 = 12;
int n1 = 5;
int result1 = m1 % n1;

int m2 = -12;
int n2 = 5;
int result2 = m2 % n2;

int m3 = 12;
int n3 = -5;
int result3 = m3 % n3;

int m4 = -12;
int n4 = -5;
int result1 = m4 % n4;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
12 % 5 = 2
-12 % 5 = -2
12 % -5 = 2
-12 % -5 = -2
1
2
3
4
  • 小结

取模结果的符号与被模数的符号一致。

# 前缀++和后缀++

a1,b1和a2,b2分别是多少?

int a1 = 10;
int b1 = ++a1;

int a2 = 10;
int b2 = a2+=;
1
2
3
4
5
a1 = 11,b1 = 11;
a2 = 11,b2 = 10;
1
2
  • 小结:

前缀++先自增1,然后再运算。

后缀++先运算,然后再自增1。

a3和b3分别是多少?

int a3 = 10;
a3++;
int b3 = a3;
1
2
3

a3=11,b3=11

a4和b4分别是多少?

int a4 = 10;
++a4;
int b4 = a4;
1
2
3

a4=11,b4=11

如果单独一行写自增运算,其实没有区别,也不会影响后面的赋值运算。

  • 注意点
short s1 = 10;
s1 = s1 + 1;//编译失败
s1 = (short)(s1 + 1);//正确的
s1++;//编译通过
1
2
3
4

小结:自增1不会改变本身变量的数据类型。

  • 问题

下面能编译通过吗?会转换为int?结果是多少?

byte b1 = 127;
b1++;
1
2

编译会通过,因为自增1不会改变本身变量的数据类型。

结果是-128,因为已经超出byte的范围了。

用二进制的方式解释:下面是127的二进制图

0 1 1 1 1 1 1 1

当自增1后

1 0 0 0 0 0 0 0

由于第一位是符号位,所以变成-128。

# 前缀--和后缀--

前缀--:先自减1,后运算

后缀--:先运算,后子减1

跟前缀++和后缀++类似,可以触类旁通。

int a1 = 10;
int b1 = --a1;
1
2

结果a1=9,b1=9。

int a2 = 10;
int b2 = a2--;
1
2

结果a2=9,b2=10。

# 练习1

int i1 = 10;
int i2 = 20;
int i = i1++;
System.out.print("i="+i);//?
System.out.print("i1="+i1);//?
i = ++i1;
System.out.print("i="+i);//?
System.out.print("i1="+i1);//?
i = i2--;
System.out.print("i="+i);//?
System.out.print("i2="+i2);//?
i = --i2;
System.out.print("i="+i);//?
System.out.print("i2="+i2);//?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
i=10;i1=11;
i=12;i1=12;
i=20;i2=19;
i=18;i2=18;
1
2
3
4

# 练习2

随意给出一个整数,打印显示它的个位数,十位数,百位数的值。

格式如下:

数字XXX的情况如下:

个位数:

十位数:

百位数:

例如:

数字153的情况如下:

个位数:3

十位数:5

百位数:1

class AreExer{
    public statis void main(String[] args){
        int num = 187;
        int bai = num / 100;
        int shi = num % 100 / 10; // int shi = num / 10 % 10
        int ge = num % 10;
        System.out.println("数字"+num+"的情况如下:");
        System.out.println("个位数:"+ge);
        System.out.println("十位数:"+shi);
        System.out.println("百位数:"+bai);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

# 赋值运算符

  • 符号:=
    • 当“=”两侧数据类型不一致时,可以使用自动类型转换或使用枪支类型转换原则进行处理。
    • 支持持续赋值。
  • 拓展赋值运算符:+=,-=,*=,/=,%=

# 演示

int i1 = 10;
int j1 = 10;

//连续赋值
int i2,j2;
i2 = j2 = 10;

int i3 = 10, j3 = 10;


int num1 =10;
num += 2;//num1 = num + 2;
System.out.println(num1);

int num2 = 12;
num2 %= 5;//num2 = num2 % 5;
System.out.println(num2);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 案例1

  • 拓展赋值运算符不会改变本身的数据类型
short s1 = 10;
//s1 = s1 + 2;//编译失败
s1 += 2;//这种写法不会改变本身的数据类型
System.out.println(s1;)
1
2
3
4

# 案例2

开发中,如果希望变量实现+2的操作,有几种方法?(前提:int num = 10)

有两种方法:

num = num + 2;
num += 2;//推荐
1
2

# 案例3

开发中,如果希望变量实现+1的操作,有几种方法?(前提:int num = 10)

有三种方法:

num = num +1;
num += 1;
num++;//推荐
1
2
3

# 案例4

思考,第一种写法和第二种写法有什么区别?

short s = 2;
s = s + 2;
s += 2;
1
2
3

第一种编译不通过。

# 案例5

思考,下面案例编译能通过吗?结果是?

int i = 1;
i *= 0.1;
System.out.println(i);
i++;
System.out.println(i);
1
2
3
4
5

编译能过,拓展赋值运算符不会改变本身的数据类型。

第一次结果是0,第二次结果是1。

# 案例6

思考,下面案例的m,n的结果是?

int m = 2;
int n = 3;
n *= m++;
1
2
3

n *= m++; => n = n * m++;

m=3,n=6

# 案例7

思考,下面案例n的结果是?

int n = 10;
n += (n++) + (++n);
1
2

可以转换为

n = n + (n++) + (++n)
    =>
    10 + 10   +  12 = 32
1
2
3

这个12是因为

第一个括号内n++所以变成11

第二个括号先自增,所以再加1 变成 12

# 比较运算符

运算符 运算 范例 结果
== 相等于 4==3 false
!= 不等于 4!=3 true
< 小于 4<3 false
> 大于 4>3 true
<= 小于或等于 4<=3 false
>= 大于或等于 4>=3 true
instanceof 检查是否是类的对象 "Hello" instanceof String true
  • 比较运算符的结果都是boolean型,也就是要么是true,要么是false。
  • 比较运算符==不能误写成=

# 案例1

int i = 10;
int j = 20;
System.out.println(i == j); // false
System.out.println(i = j); //20,
1
2
3
4

先将j赋值给i,然后输出i

# 案例2

boolean b1 = true;
boolean b2= false;
System.out.println(b2 == b1);//fasle
System.out.println(b2 = b1);//true
1
2
3
4

# 逻辑运算符

逻辑运算符都是操作boolean类型的变量。

  • &:逻辑与
  • |:逻辑或
  • !:逻辑非
  • &&:短路与
  • ||:短路或
  • ^:落于异或
a b a&b a&&b a|b a||b !a a^b
true true true true true true false false
true false false false true true false true
false true false false true true true true
false false false false false false true false

# 区分 &与 &&

&和&&的作用:左右两边都为true时,才返回true,否则返回false。

相同点:运算结果相同。当符号左边是true时,&和&&都会执行符号右边的运算。

区别:当符号左边是false时,&继续执行符号右边的运算,&&不再执行符号右边的运算。

boolean b1 = false;
int num1 = 10;
if(b1 & (num1++) > 0){
    System.out.println("我现在在北京");
}else{
    System.out.println("我现在在南京");
}
System.out.println("num1="+num1);

boolean b2 = false;
int num2 = 10;
if(b2 && (num2++) > 0){
    System.out.println("我现在在北京");
}else{
    System.out.println("我现在在南京");
}
System.out.println("num2="+num2);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

输出结果:

我现在在南京
num1=11
我现在在南京
num2=10
1
2
3
4

# | 与 ||

| 与 ||的作用:只要符号左右两边任意一个为true,则返回true,只有两边都不是true时,才返回false。

相同点:运算结果相同。当符号左边是false时,|和||都会执行符号右边的运算。

区别:当符号左边是true时,|继续执行符号右边的运算,||不再执行符号右边的运算。

boolean b1 = true;
int num1 = 10;
if(b1 | (num1++) > 0){
    System.out.println("我现在在北京");
}else{
    System.out.println("我现在在南京");
}
System.out.println("num1="+num1);

boolean b2 = true;
int num2 = 10;
if(b2 || (num2++) > 0){
    System.out.println("我现在在北京");
}else{
    System.out.println("我现在在南京");
}
System.out.println("num2="+num2);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

输出结果:

我现在在北京
num1=11
我现在在北京
num2=10
1
2
3
4

在开发中,推荐使用|| 和&&,因为当条件达成时,不会继续运算,效率更高。

# 练习1

最终x,y分别是多少?

int x = 1;
int y = 1;
if(x++ == 2 & ++y == 2){
    x = 7;
}
1
2
3
4
5

x=2,y=2

# 练习2

最终x,y分别是多少?

int x =1,y=1;
if(x++ == 2 && ++y == 2){
    x = 7;
}
1
2
3
4

x=2,y=1

# 练习3

最终x,y分别是多少?

int x = 1, y = 1;
if(x++ == 1 | ++y == 1){
	x = 7;
}
1
2
3
4

x=7,y=2

# 练习4

最终x,y分别是多少?

int x = 1, y = 1;
if(x++ == 1 || ++y == 1){
	x = 7;
}
1
2
3
4

x=7,y=1

# 面试题

请问z的最终结果是多少?

class Test{
    public statis void main(String[] args){
       boolean x = true;
        boolean y = false;
        short z = 42;
        //if(y == true)
        if ((z++ == 42) && (y = true)) z++;
        if ((x = false) || (++z == 45)) z++;
        System.out.println("z=" + z);
    }
}
1
2
3
4
5
6
7
8
9
10
11

最终:z=46,如果去掉注释是43。

  • 先来看第7行代码:&&只要两边都是true就执行后面的代码

左边括号:z++ == 42,是后缀++,所以先运算再自增,此时z=42,所以返回true,然后自增1,z=43

右边括号:y=true是赋值操作,然后返回true

两边都是true,执行z++,z= 43+1 = 44

  • 第8行代码:|| 只要一个是true就执行后面的代码

左边括号:x = false是赋值操作,返回false

右边括号:++z == 45,这是前缀++,所以先自增再运算,此时z=44,自增1,z=45,然后45==45,返回true

右边括号返回true,所以执行z++,此时z=45+1=46。

去掉注释这里就不展开讲了,道理都一样的。

# 位运算符

位运算符是直接对整数的二进制进行的运算。在日常开发中实际上很少使用位运算符,但是看后期看底层源码的时候会出现位运算符,所以还是需要了解一下。

运算符 运算 范例 细节
<< 左移 3 << 2 = 12 => 3 * 2 * 2 = 12 空位补0,被移除的高位丢弃,空缺位补0.
>> 左移 3 >> 1 = 1 => 3 / 2 = 1 被移位的二进制最高位是0,右移后,空缺位补0;最高位是1,空缺位补1。
>>> 无符号右移 3 >>> 1 = 1 => 3 / 2 = 1 被移位二进制最高位无论是0或者是1,空缺位都用0补。
& 与运算 6 & 3 =2 二进制位进行&运算,只有1&1时结果是1,否则是0。
| 或运算 6 | 3 = 7 二进制位进行|运算,只有0|0时结果是0,否则是1。
^ 异或运算 6 ^ 3 = 5 相同二进制位进行^运算,结果是0:1^1=0,0^0=0
不同二进制位进行^运算结果是1。1^0=1,0^1=1
~ 取反运算 ~ 6 = -7 正数取反,各二进制码按补码各位取反
负数取反,各二进制码按补码各位取反

注意:&,|如果左右是boolean类型,则是逻辑运算符,不是位运算符。

# << 左移

class BitTest{
    public statis void main(String[] args){
        int i = 12;
        System.out.println("i <<< 2 :" + (i<<2));//84
    }
}
1
2
3
4
5
6

通过计算器(当然也可以自己计算),获取到21的二进制是0001 0101

image-20201204221136990

0 0 0 1 0 1 0 1

当上面二进制数,向做移动两位后就变成01 0101,后面自动补0,就变成下面这样:

0 1 0 1 0 1 0 0
//原来的算法
1*2^0 + 1*2^2 + 1*2^4 = 21;

//现在
1*2^2 + 1*2^4 + 1*2^6 = 84 => 21*2^2    
1
2
3
4
5

那试试左移三位:

class BitTest{
    public statis void main(String[] args){
        int i = 12;
        System.out.println("i <<< 2 :" + (i<<2));//84
        System.out.println("i <<< 3 :" + (i<<3));//168
    }
}
1
2
3
4
5
6
7
  • 小结

对比以前和现在,其实x变量左移2位,其实就是x*2^2,那么x变量左移n位的公式是:

x*2^n
1

但是这里还是有一个小问题,在java中,整形是32位二进制数,那么21的二进制数0001 0101,前面一共有27个0,那如果左移27位,会发生什么事情?

class BitTest{
    public statis void main(String[] args){
        int i = 12;
        System.out.println("i <<< 2 :" + (i<<2));//84
        System.out.println("i <<< 3 :" + (i<<3));//168
        System.out.println("i <<< 27 :" + (i<<27));//-1476395008 在二进制中最高位都变成1了。
    }
}
1
2
3
4
5
6
7
8

那么触类旁通

总结:

  1. 位运算符都是操作整型的数据
  2. << 左移,在一定范围内,每向左移1位,相当于 * 2。
  3. >>右移,在一定范围内,每向右移1位,相当于 / 2。

面试题:最高效方式的计算2 * 8 ? 2 << 3 或者 8 << 1

# & 与运算

二进制位进行&运算,只有1&1时结果是1,否则是0。

比如12 & 5

  • 12
0 0 0 0 1 1 0 0
  • 5
0 0 0 0 0 1 0 1

小技巧,0当成false,1当成true,每位0&1 推算二进制码

  • 结果
0 0 0 0 0 1 0 0
1*2^2 = 4
1

所以结果是4

用java计算也是4。

public class BitTest {
    public static void main(String[] args) {
        int m = 12;
        int n = 5;
        System.out.println("m & n :" + (m & n));//4
    }
}
1
2
3
4
5
6
7

# | 或运算

二进制位进行|运算,只有0|0时结果是0,否则是1。

比如12 | 5 = 13

public class BitTest2 {
    public static void main(String[] args) {
        int m = 12;
        int n = 5;
        System.out.println("m | n :" + (m | n));//13
    }
}
1
2
3
4
5
6
7

用二进制来推算

那么跟上面与运算一样,0代表false,1代表true,任意一个是1就是1,两个都为0才是0

  • 12
0 0 0 0 1 1 0 0
  • 5
0 0 0 0 0 1 0 1
  • 结果
0 0 0 0 1 1 0 1
1*2^0 + 1*2^2 + 1*2^3 = 13
1

# ^ 异或运算

在二进制运算中,只要相异,结果是1,相同则是0。

public class BitTest3 {
    public static void main(String[] args) {
        int m = 12;
        int n = 5;
        System.out.println("m ^ n :" + (m ^ n));//9
    }
}
1
2
3
4
5
6
7

用二进制来推算:

  • 12
0 0 0 0 1 1 0 0
  • 5
0 0 0 0 0 1 0 1
  • 结果
0 0 0 0 1 0 0 1
1*2^0 + 1*2^3 = 9
1

# ~ 取反运算

正数取反,各二进制码按补码各位取反
负数取反,各二进制码按补码各位取反

包括符号位在内都要取反

public class BitTest4 {
    public static void main(String[] args) {
        int m = 6;
        System.out.println(~m);// -7
    }
}
1
2
3
4
5
6

二进制推算:

0000 0000 0000 0000 0000 0000 0000 0110 6
1111 1111 1111 1111 1111 1111 1111 1001 ~6 = -7

如果将上面例子的m改为-7,得出的结果是6,这说明6和-7互为取反。

# 练习

交换两个变量的值

  • 方式一,使用临时变量
/*
交换两个变量的值
 */
public class ChangeTest1 {
    public static void main(String[] args) {
        int num1 = 10;
        int num2 = 20;
        int temp = num1;
        num1 = num2;
        num2 = temp;
        System.out.println(num1);
        System.out.println(num2);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  • 方式二,
/*
交换两个变量的值
 */
public class ChangeTest2 {
    public static void main(String[] args) {
        int num1 = 10;
        int num2 = 20;
        num1 = num1 + num2;
        num2 = num1 - num2;
        num1 = num1 - num2;
        System.out.println(num1);
        System.out.println(num2);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

虽然方式一简单粗暴,方式二不用临时变量,节省内存空间。但是实际开发中还是选择方式一,方式二有一个弊端,有可能两者相加的时候超出存储范围,而且只用于数值型,要是非数值型不好办了,而方式一适用于任何类型的变量。

  • 方式三:使用位运算符。
/*
交换两个变量的值
 */
public class ChangeTest3 {
    public static void main(String[] args) {
        int num1 = 10;
        int num2 = 20;
        num1 = num1 ^ num2;
        num2 = num1 ^ num2;
        num1 = num1 ^ num2;
        System.out.println(num1);
        System.out.println(num2);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

方式三的局限性跟方式二的一样。

# 三元运算符

# 格式

(条件表达式)?表达式1:表达式2;

当结果true,执行表达式1,否则执行表达式2。

  • 条件表达还是必须是boolean类型
  • 表达式1和表达式2必须要求是同一返回类型。
import java.util.Random;

public class IfElseTest {
    public static void main(String[] args) {
        //获取两个整数的较大值
        int m = new Random().nextInt(100);
        System.out.println(m);
        int n = new Random().nextInt(100);
        System.out.println(n);
        int max = m > n ? m : n;
        System.out.println("max:" + max);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
  • 输出:
97
77
max:97
1
2
3

三元运算符是可以嵌套的,比如获取三个数的最大值

import java.util.Random;

public class IfElseTest {
    public static void main(String[] args) {
        int m = new Random().nextInt(100);
        System.out.println(m);
        int n = new Random().nextInt(100);
        System.out.println(n);
        int k = new Random().nextInt(100);
        System.out.println(k);
        int max = m > n ? (m>k?m:k) : (n>k?n:k);
        System.out.println("max:" + max);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

输出:

12
48
84
max:84
1
2
3
4

凡是可以使用三元运算符的地方都可以改写为if-else,反之,不一定成立。

如果程序某处既可以使用三元运算符又可以用if-else结构,那么优先选择三元运算符,因为简洁,效率高。

# 运算符的优先级

当我们学完了所有的运算符,知道运算符是有优先级的。

下面是运算符的顺序,从高到低

. () {} ; ,
R => L ++ -- ~ !(data type)
L => R * / %
L => R + -
L => R << >> >>>
L => R < > <= >= instanceof
L => R == !=
L => R &
L => R ^
L => R |
L => R &&
L => R ||
R => L ? :
R => L = *= /= %=
R => L += -= <<= >>=
R => L >>>= &= ^= |=

只有弹幕运算符、三元运算符、赋值运算符是从右向左运算的。

可以看到,实际开发中,直接使用小括号来控制就行了。

帮我改善此页面 (opens new window)
#运算符
上次更新: 2020/12/18, 12:50:58
进制
Scanner类

← 进制 Scanner类→

最近更新
01
zabbix学习笔记二
02-28
02
zabbix学习笔记一
02-10
03
Linux访问不了github
12-08
更多文章>
Theme by Vdoing | Copyright © 2020-2022 Saul.J.Wu | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式