因为 Java 是跨平台语言,因此 JVM 表现下的基础数据字节长度其实都是一致的。算法
int:4 个字节。 (1个字节是8位)测试
short:2 个字节。加密
long:8 个字节。spa
byte:1 个字节。code
float:4 个字节。orm
double:8 个字节。it
char:2 个字节。io
boolean:boolean属于布尔类型,在存储的时候不使用字节,仅仅使用 1 位来存储,范围仅仅为0和1,其字面量为true和false。form
原码 反码 补码
咱们已经知道了一个 int 型数值是 4 个字节。每一个字节有 8 位。但对于一个 int 或者其它整数类型如 (long)的数值而言还要注意的是,它的最高位是符号位。class
最高位为0表示正数。
最高位为1表示负数
原码 将一个数字转换成二进制就是这个数值的原码。
int a = 5; //原码 0000 0000 0000 0000 0000 0000 0000 0101
int b = -3; //原码 1000 0000 0000 0000 0000 0000 0000 0011
反码
分两种状况:正数和负数
正数 正数的反码就是原码。
负数 负数的反码是在原码的基础上,符号位不变 其它位都取反。
5 的原码:0000 0000 0000 0000 0000 0000 0000 0101
-3 的原码:1000 0000 0000 0000 0000 0000 0000 0011
-3 的反码:1111 1111 1111 1111 1111 1111 1111 1100
补码
仍然分正数和负数两种状况
正数 正数的补码就是原码。
负数 负数的补码在反码的基础上加1。
5 的补码:0000 0000 0000 0000 0000 0000 0000 0101
-3 的反码:1111 1111 1111 1111 1111 1111 1111 1100
-3 的补码: 1111 1111 1111 1111 1111 1111 1111 1101
计算机在进行数值运算的时候,是经过补码表示每一个数值的。
经过程序运行能够验证
public class Code { public static void main(String[] args) { int a = 5; int b = -3; String formata = String.format("%032d", Integer.parseInt(Integer.toBinaryString(a))); System.out.println(formata); System.out.println(Integer.toBinaryString(b)); } }
运行结果
00000000000000000000000000000101
11111111111111111111111111111101
若是a和b相加
100000000000000000000000000000010 此处为33位,溢出,第一位1放弃,变为
00000000000000000000000000000010 恰好为2, 5-3=2 没毛病。
程序验证
public class Code { public static void main(String[] args) { int a = 5; int b = -3; String formata = String.format("%032d", Integer.parseInt(Integer.toBinaryString(a))); System.out.println(formata); System.out.println(Integer.toBinaryString(b)); System.out.println(String.format("%032d",Integer.parseInt(Integer.toBinaryString(a + b)))); } }
运行结果
00000000000000000000000000000101
11111111111111111111111111111101
00000000000000000000000000000010
& 与运算符
规则 与运算时,进行运算的两个数,从最低位到最高位,一一对应。若是某 bit 的两个数值对应的值都是 1,则结果值相应的 bit 就是 1,不然为 0.
0 & 0 = 0,
0 & 1 = 0,
1 & 1 = 1
3 & 5 = 1 这是由于 (高位0省略)
0000 0011
&
0000 0101
=
0000 0001
按照规则,将两个数值按照低位到高位一一对齐运算,由于只有第 0 位都为 1,因此计算结果为 1.
程序验证
public class Code { public static void main(String[] args) { int a = 5; int b = 3; String formata = String.format("%032d", Integer.parseInt(Integer.toBinaryString(a & b))); System.out.println(formata); System.out.println(a & b); } }
运行结果
00000000000000000000000000000001
1
判断一个数的奇偶性能够对1取与运算,结果为1为奇数,0为偶数
| 或运算符
规则 或运算时,进行运算的两个数,从最低位到最高位,一一对应。若是某 bit 的两个数值对应的值只要 1 个为 1,则结果值相应的 bit 就是 1,不然为 0。
0 | 0 = 0,
0 | 1 = 1,
1 | 1 = 1
3 | 5 = 7 这是由于 (高位省略)
0000 0011
|
0000 0101
=
0000 0111
程序验证
public class Code { public static void main(String[] args) { int a = 5; int b = 3; String formata = String.format("%032d", Integer.parseInt(Integer.toBinaryString(a | b))); System.out.println(formata); System.out.println(a | b); } }
运行结果
00000000000000000000000000000111
7
规则 对操做数的每一位进行操做,1 变成 0,0 变成 1。
~5 => 0000 0101 ~ => 1111 1010
5取反后十进制数为 (高位省略)
1111 1010 (最高位为1,确定为负数,此时为补码)
1111 1001 (补码转反码-1)
1000 0110 (反码转原码,此时为-6)
因此5取反后为-6
程序验证
public class Code { public static void main(String[] args) { int a = 5;; System.out.println(Integer.toBinaryString(~a)); System.out.println(~a); } }
运行结果
11111111111111111111111111111010
-6
^ 异或运算符
规则 两个操做数进行异或时,对于同一位上,若是数值相同则为 0,数值不一样则为 1。
1 ^ 0 = 1,
1 ^ 1 = 0,
0 ^ 0 = 0;
3 ^ 5 = 6,这是由于 (高位省略)
0000 0011
^
0000 0101
=
0000 0110
程序验证
public class Code { public static void main(String[] args) { int a = 5; int b = 3; System.out.println(String.format("%032d",Integer.parseInt(Integer.toBinaryString(a ^ b)))); System.out.println(a ^ b); } }
运行结果
00000000000000000000000000000110
6
值得注意的是 3 ^ 5 = 6,而 6 ^ 5 = 3
0000 0110
^
0000 0101
=
0000 0011
public class Code { public static void main(String[] args) { int a = 5; int b = 6; System.out.println(String.format("%032d",Integer.parseInt(Integer.toBinaryString(a ^ b)))); System.out.println(a ^ b); } }
运行结果
00000000000000000000000000000011
3
针对这个特性,咱们能够将异或运算做为一个简单的数据加密的形式。好比,将一个mp4文件全部数值与一个种子数值进行异或获得加密后的数据,解密的时候再将数据与种子数值进行异或一次就能够了。
因此说异或运算能够做为简单的加解密运算算法。能够说不少对称加密算法都是根据异或进行扩展的,以后在DES,AES加密原理中会有说到。
>> 右移运算符
规则 a >> b 将数值 a 的二进制数值从 0 位算起到第 b - 1 位,总体向右方向移动 b 位,符号位不变,正数高位空出来的位补数值 0,负数补1。
5 >> 1 ===> 0000 0000 0000 0101 >> 1 = 0000 0000 0000 0010 = 2
7 >> 2 ===> 0000 0000 0000 0111 >> 2 = 0000 0000 0000 0001 = 1
9 >> 3 ===> 0000 0000 0000 1001 >> 3 = 0000 0000 0000 0001 = 1
11 >> 2 ===> 0000 0000 0000 1011 >> 2 = 0000 0000 0000 0010 = 2
规律:a >> b = a / ( 2 ^ b ) ,因此 5 >> 1= 5 / 2 = 2,11 >> 2 = 11 / 4 = 2。 (此处2 ^ b为2的b次方)
咱们来看一下负数的右移
-5 >> 1
1000 0000 0000 0101 (-5原码)
1111 1111 1111 1010 (-5反码)
1111 1111 1111 1011 (-5补码)
1111 1111 1111 1101 右移1位,符号位不变,负数高位补1
1111 1111 1111 1100 补码转反码
1000 0000 0000 0011 反码转原码,因此-5右移1位为-3
程序验证
public class Code { public static void main(String[] args) { int a = -5; System.out.println(Integer.toBinaryString(a >> 1)); System.out.println(a >> 1); } }
运行结果
11111111111111111111111111111101
-3
右移还有一个无符号右移>>>,对于正数来讲,>>和>>>没有什么区别,但对于负数来讲差异倒是巨大的
-5 >>> 1 (此处必须使用32位来计数)
1000 0000 0000 0000 0000 0000 0000 0101 -5原码
1111 1111 1111 1111 1111 1111 1111 1010 -5反码
1111 1111 1111 1111 1111 1111 1111 1011 -5补码
0111 1111 1111 1111 1111 1111 1111 1101 无符号右移1位,高位补0
由于此时高位为0,已经为正数了,因此0111 1111 1111 1111 1111 1111 1111 1101就是结果的原码,十进制为2147483645
具体为(2 ^ 30)+(2 ^ 29) +(2 ^ 28)+(2 ^ 27)+(2^ 26)+(2 ^ 25)+(2 ^ 24)+(2 ^ 23)+(2 ^ 22)+(2 ^ 21)+(2 ^ 20)+(2 ^ 19)+(2 ^ 18)+(2 ^ 17)+(2 ^ 16)+(2 ^ 15)+(2 ^ 14)+(2 ^ 13)+(2 ^ 12)+(2 ^ 11)+(2 ^ 10)+(2 ^ 9)+(2 ^ 8)+(2 ^ 7)+(2 ^ 6)+(2 ^ 5)+(2 ^ 4)+(2 ^ 3)+(2 ^ 2)+1 =2147483645
程序验证
public class Code { public static void main(String[] args) { int a = -5; System.out.println("0" + Integer.toBinaryString(a >>> 1)); System.out.println(a >>> 1); } }
运行结果
01111111111111111111111111111101
2147483645
<< 左移运算符
规则 a << b 将数值 a 的二进制数值从 0 位算起到第 b - 1 位,总体向左方向移动 b 位,符号位不变,低位空出来的位补数值 0。
5 << 1 ===> 0000 0000 0000 0101 << 1 = 0000 0000 0000 1010 = 10
7 << 2 ===> 0000 0000 0000 0111 << 2 = 0000 0000 0001 1100 = 28
9 << 3 ===> 0000 0000 0000 1001 << 3 = 0000 0000 0100 1000 = 72
11 << 2 ===> 0000 0000 0000 1011 << 2 = 0000 0000 0010 1100 = 44
规律: a << b = a * (2 ^ b) (此处2 ^ b为2的b次方)
综合上面两个能够看到,若是某个数值(正数)右移 n 位,就至关于拿这个数值去除以 2 的 n 次幂。若是某个数值(正数)左移 n 位,就至关于这个数值乘以 2 ^ n。
咱们来看一下负数左移
-5 << 1
1000 0000 0000 0101 (-5原码)
1111 1111 1111 1010 (-5反码)
1111 1111 1111 1011 (-5补码)
1111 1111 1111 0110 左移1位
1111 1111 1111 0101 补码转反码
1000 0000 0000 1010 反码转原码,为-10
因此-5 << 1 = -10
程序验证
public class Code { public static void main(String[] args) { int a = -5; System.out.println(Integer.toBinaryString(a << 1)); System.out.println(a << 1); } }
运行结果
11111111111111111111111111110110
-10
由此看来,负数左移规律 -a << b = -a * (2 ^ b) (此处2 ^ b为2的b次方)
检测第K位是否为1 (如下运行结果均未补高位0)
已知数n,检测其第K位(右起)是否为1,能够用如下表达式:
n & (1 << k - 1) 结果为0,说明第K位为0;结果不为0,说明第K位为1。
好比我要检测965的二进制第3位是否为1
public class Code { public static void main(String[] args) { int a = 965; int k = 1 << 3 - 1; System.out.println(Integer.toBinaryString(a)); System.out.println(Integer.toBinaryString(a & k)); } }
运行结果
1111000101
100
说明第3位为1
若是我要检测第4位
public class Code { public static void main(String[] args) { int a = 965; int k = 1 << 4 - 1; System.out.println(Integer.toBinaryString(a)); System.out.println(Integer.toBinaryString(a & k)); } }
运行结果
1111000101
0
说明第4位为0
给一个数的第K位设置为1
对于一个给定的操做数n,设置其第k位为1,能够用
n | (1 << k - 1)
好比我要设置965的第4位为1
public class Code { public static void main(String[] args) { int a = 965; int k = 1 << 4 - 1; System.out.println(Integer.toBinaryString(a)); System.out.println(Integer.toBinaryString(a | k)); System.out.println(a | k); } }
运行结果
1111000101
1111001101
973
第k位清零
将给定操做数n的第k位清零,能够用表达式
n & ~(1 << k - 1)
好比我要将965的第3位清零
public class Code { public static void main(String[] args) { int a = 965; int k = 1 << 3 - 1; System.out.println(Integer.toBinaryString(a)); System.out.println(Integer.toBinaryString(a & ~k)); System.out.println(a & ~k); } }
运行结果
1111000101
1111000001
961
切换第k位(1变0,0变1)
切换给定操做数n的第k位,能够用表达式
n ^ (1 << k-1)
好比我要切换965的第4位
public class Code { public static void main(String[] args) { int a = 965; int k = 1 << 4 - 1; System.out.println(Integer.toBinaryString(a)); System.out.println(Integer.toBinaryString(a ^ k)); System.out.println(a ^ k); } }
运行结果
1111000101
1111001101
973
切换第3位
public class Code { public static void main(String[] args) { int a = 965; int k = 1 << 3 - 1; System.out.println(Integer.toBinaryString(a)); System.out.println(Integer.toBinaryString(a ^ k)); System.out.println(a ^ k); } }
运行结果
1111000101
1111000001
961
切换值为1的最右位
切换给定操做数n的值为1的最右位,能够使用表达式
n & n - 1
仍是965
public class Code { public static void main(String[] args) { int a = 965; int k = a - 1; System.out.println(Integer.toBinaryString(a)); System.out.println(Integer.toBinaryString(a & k)); System.out.println(a & k); } }
运行结果
1111000101
1111000100
964
隔离值为1的最右位
隔离给定操做数n的值为1的最右位,能够使用表达式n & -n.
此次咱们使用966
public class Code { public static void main(String[] args) { int a = 966; int k = -a; System.out.println(Integer.toBinaryString(a)); System.out.println(Integer.toBinaryString(a & k)); System.out.println(a & k); } }
运行结果
1111000110
10
2
最右位为1是在第2位上,因此隔离为10,末位0无心义,只是为了说明最右位为1是第2位而已。
隔离值为0的最右位
隔离给定操做数n的值为0的最右位,能够使用表达式
~n & n + 1
此次依然使用965
public class Code { public static void main(String[] args) { int a = 965; int k = a + 1; System.out.println(Integer.toBinaryString(a)); System.out.println(Integer.toBinaryString(~a)); System.out.println(Integer.toBinaryString(k)); System.out.println(Integer.toBinaryString(~a & k)); System.out.println(~a & k); } }
运行结果
1111000101
11111111111111111111110000111010
1111000110
10
2
结果10仅表示最右位为0的为第2位
检查某个数是不是2的幂
给定一个数n,检查其是否知足2 ^ n(2的n次方)的形式,能够使用表达式
if (n & n - 1 == 0)
咱们使用968来测试一下
public class Code { public static void main(String[] args) { int a = 968; int k = a - 1; System.out.println(Integer.toBinaryString(a)); if ((a & k) == 0) { System.out.println("a是2的幂"); }else { System.out.println("a不是2的幂"); } } }
运行结果
1111001000
a不是2的幂
咱们用32768来测试一下
public class Code { public static void main(String[] args) { int a = 32768; int k = a - 1; System.out.println(Integer.toBinaryString(a)); if ((a & k) == 0) { System.out.println("a是2的幂"); }else { System.out.println("a不是2的幂"); } } }
运行结果
1000000000000000
a是2的幂
将某个数乘以2的幂
对于一个给定的数n,将其乘以2 ^ k (2的k次方),能够使用表达式
n << k
假如咱们用965乘以2的5次方
public class Code { public static void main(String[] args) { int a = 965; int k = 5; System.out.println(Integer.toBinaryString(a)); System.out.println((int)(a * Math.pow(2,k))); System.out.println(a << k); } }
运行结果
1111000101
30880
30880
将某个数除以2的幂
给定操做数n,将其除以2 ^ k (2的k次方),能够使用表达式
n >> k
假如咱们用965除以2的5次方
public class Code { public static void main(String[] args) { int a = 965; int k = 5; System.out.println(Integer.toBinaryString(a)); System.out.println((int)(a / Math.pow(2,k))); System.out.println(a >> k); } }
运行结果
1111000101
30
30
找到给定操做数的模
给定操做数n,计算其k(这里k通常为2的幂)的模(% k),能够使用表达式n & k - 1
取数965对2的5次方取模
public class Code { public static void main(String[] args) { int a = 965; int k = 5; System.out.println(Integer.toBinaryString(a)); System.out.println((int)(a % Math.pow(2,k))); System.out.println(a & ((1 << k) - 1)); } }
运行结果
1111000101
5
5
位值1的计数
统计位值1的计数方法有不少种,这里主要讲2种
一、按位处理
好比计算998的二进制数中有多少个1
public class Location { public static void main(String[] args) { int n = 998; System.out.println(Integer.toBinaryString(n)); int count = 0; while (n != 0) { //count老是加末位的0或者1 count += n & 1; //n右移1位 n >>= 1; } System.out.println(count); } }
运行结果
1111100110
7
二、使用切换方法:n & n - 1
public class Count { public static void main(String[] args) { int n = 998; System.out.println(Integer.toBinaryString(n)); int count = 0; while (n != 0) { count++; //n和n-1每与操做一次就会消耗掉n的一个前位1 n &= n - 1; } System.out.println(count); } }
运行结果
1111100110 7