基本运算符 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
右移 >>
位运算的场景
假设你有猫的 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'); }