ZB-008-java运算系统

基本运算符

1
2
3
4
5
6
7
8
9
10
11
+
-
*
/
% 取余 取余运算是带符号运算

+=
-=
*=
/=
%=
1
2
3
4
5
6
7
8
9
10
11
int a = 1 + 1; // int
int b = 5 / 2; // int

5.0/2; double


// 是否是奇数
public static boolean isOdd(int n){
reutrn n % 2 !=0;
// n % 2 == 1; 取余是带符号运算
}

自增自减

  • ++
  • --

i++++i 不一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int a = 0;
int b = 0;

System.out.println(a++); // 0
System.out.println(++b); // 1


a++ 分两步
1. a = 0 作为表达式的值
2. a = a + 1

++b 分两步
1. a = a + 1 作为表达式的值
2. 把结果作为表达式的值

比较运算符

  • >
  • <
  • >=
  • <=
  • ==
  • !=

逻辑运算符

  • &&
  • ||
  • !
  • 短路特性
1
2
3
4
5
6
7
8
9
10
Boolean b = ?

// 1 true => ture
// 2 false => false
// 3 null => false

// 使用短路特性,如果不是 b = null的时候报错 空指针
if( b != null && b){

}

三元运算

  • ?:
1
2
int a = 3;
int res = a > 3 ? 1: -1;

位运算符

  • ~按位取反
  • & &= 按位与
  • | |= 按位或
  • ^ ^= 异或
  • << <<= 带符号左移
  • >> >>= 带符号右移
  • >>> >>>= 无符号右移(总是补0)

取反

计算机中 负数使用 补码表示的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int a = 3;
存在计算机里是 32位 4字节
00000000 00000000 00000000 00000011

~a 得到什么? -4 为啥

补码 ===> 按位取反
0000 0011 数字3
第一步 按位取反
1111 1100
第二步 补码 = 反码+1
1111 1101 得到数字-3

如果首位为“符号位” 那么
00000011 是数字 3
10000011 是数字 -3

如果是无符号就要用到补码了

烧脑的来了

+ - * / 是在电脑中的 CPU 负责运算的,是一种硬件电路

  • 对于基本的运算 + - 我们能不能统一为一种电路

    1
    就是一种 加法 的运算 既可以处理 "+" 也可以处理 “-”
  • 这种背景下

  • 补码出现了

例子 5 - 3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
5 - 3 等价于 5 + (-3)

这样就可以用 “加法” 来完成 5 - 3 的运算


5 的二进制表示
0000 0101

3 的二进制表示
0000 0011

-3 的二进制表示
0000 0011 数字 3 取反
1111 1100 反码 计算机里代表 -4
1111 1101 补码

5 + (-3)
0000 0101
1111 1101
-------------
0000 0010

& &= 按位与| |= 按位或

1
2
3
4
5
6
7
8
9
10
11
12
13
14
3 按位与 5

0000 0011 => 5
0000 0101 => 3
————————————
0000 0001 => 1


6 按位或 8

0000 0110 => 6
0000 1000 => 8
————————————
0000 1110 => E

异或

1
2
3
4
5
6
7
5 异或 7
0000 0101 => 5
0000 0111 => 7
_____________
0000 0010 => 2

不进位加法

NB的异或公式

  • 用处就是有些算法题可以用来加速计算
    1
    2
    在一个很长的数组里找一个数字 独立存在的数字 答案是 3
    [11,22,33,44,12,12,4,3,4] 把所有数字全部异或一次
1
2
3
x ^ y ^ y = x

x ^ x = 0

左移<<

  • 左移的好处就是比乘法操作快
1
2
3
4
5
6
7
8
9
数字 5
0000 0101
<<
0000 1010 得到 10

每左移一位 实际就是 *2

5左移1位 ==> 5*2 = 10
5左移2位 ==> 5*4 = 20

右移 >>

  • 带符号 首位补 原先的符号位
  • 不带符号 首位补0

位运算的场景

  • Modifier类

假设你有猫的 32个boolean值属性 萌不萌 胖不胖

  • 第一种方式 存放32个 boolean 成员属性,这样导致每个布尔值(1字节)占据 32字节
  • 另一种选择:一个int是 32位01组成,每位代表之前的boolean属性 这样 从32个字节 变成了 4字节
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Cat{
public static final int 萌不萌 = 0x00000001;
public static final int 胖不胖 = 0x00000002;
public static final int 爱吃鱼 = 0x00000004;
public static final int 爱老鼠 = 0x00000008;
// 等等...

public static int final state =
Cat.萌不萌 | Cat.胖不胖 | Cat.爱吃鱼 | Cat.爱老鼠;
}

0x00000001 => 16进制最后一位换成4位2进制数 0001 萌不萌
0x00000002 => 16进制最后一位换成4位2进制数 0010 胖不胖
0x00000004 => 16进制最后一位换成4位2进制数 0100 爱吃鱼
0x00000008 => 16进制最后一位换成4位2进制数 1000 爱老鼠

正好 每个位置的 “ 1 ” 都是错开的

此时设置属性的时候只要 “或”
public static int final state =
Cat.萌不萌 | Cat.胖不胖 | Cat.爱吃鱼 | Cat.爱老鼠;
// 按位或之后得到 0000 1111

那么我们如何知道 Cat 萌不萌 呢?
只需要它让一个定义好的数字 & 运算就行了

// 萌不萌?
1111 // 全部的 state
0001 // 萌不萌的实际取值
// 按位与的结果为
res = 0001

结果只要比较 res 是不是 0 即可
  • 设置值时候使用 按位或 |
  • 取值值时候使用 按位与 &

参考练习题

字符串加法操作

1
2
3
4
5
int a = 1;
int b = 2;
System.out.println("a=" + a + ",b=" + b);

// 你可以给 StringBuilder 打断点,因为字符串是不可变字符序列,所以 频繁拼接很浪费空间,于是会使用 StringBuilder 对一块内存进行操作
  • 当你进行字符串加法的时候:它会调用这个对象的 toString, (a.toString(),b.toString())
  • 然后在你进行很长的字符串加法时候,JDK内部会自动的帮你转化成 StringBuilder 调用,减轻内存压力,提高性能。

判断 char 类型 合法字符问题

1
2
3
4
5
6
7
// 一个合法的十六进制的字符是:字符0-9,以及字符A/a/B/b/C/c/D/d/E/e/F/f (大小写都是合法的)
// 编写一个方法,给定一个字符,若是合法的十六进制字符,返回true,否则返回false
public static boolean isValidHexCharacter(char ch) {
return (ch >= '0' && ch <= '9')
|| (ch >= 'a' && ch <= 'f')
|| (ch >= 'A' && ch <= 'F');
}