目录:
3.Java &、&&、|、||、^、<<、>>、~、>>>等运算符
1.如何理解二进制、十进制、十六进制
1.1 十进制的理解:
生活中我们遇到的绝大部分数据都是十进制的,比如7、24、30、365等,如果把它们按照个位、十位、百位分解,可以这样表示:
数值 | 个位 | 十位 | 百位 |
7 | 7 | 0 | 0 |
24 | 4 | 2 | 0 |
30 | 0 | 3 | 0 |
365 | 5 | 6 | 3 |
把表格中的数值,用数学运算表达式表示是这样的:
数值 | 分解 |
7 | 1*7 |
24 | 10*2+1*4 |
30 | 10*3+1*0 |
365 | 100*3+10*6+1*5 |
十进制是“逢十进一”的规则,例如数字9,已经是个位上能够表示的最大的数值了,如果要表示更大的数值,就需要突破个位,使用十位来组成一个“数字串”来表示了,比如10(十位上是1,个位上是0),因此对十进制的的加法可以这样理解:
例如:8+7=15
- =8+(2+5)
- =8+2+5
- =10+5
- =15
1.2二进制的理解
二进制是“逢二进一”,也就是说二进制只有0、1两个数字来表示,遇到2时就需要向高位“进一”了,比如“24”使用二进制来表示就是“0001 1000”
如果在书写或者程序中使用这样的表示方式,就太啰嗦了,且不便于 其他进制区分,所以有了一个来表示二进制的规定,就是0b(十六进制是0x来表示),比如刚才的“0000 0000 0001 1000”可以使用“0b00011000”。
在十进制中像365这样的数值,我们可以这样理解:
- 365=100*3+10*6+1*5
相应的在二进制中也有类似的规则,区别是:
十进制是“逢十进一”,采用“个/十/百/千/......”的进位递增;
二进制是“逢二进一”,采用“1/2/4/8/16/......”的进位递增;
下面以几个二进制数值来举例:
把上面表格的表达方式用数学运算表达式表示的话,是这样的:
其实根本就在于,十进制中相邻进制之间相差10倍,而二进制中相邻进制之间相差的是2倍,只要记住这点,然后使用十进制相似的规则去套就行了。
1.3十六进制的理解
在理解二进制的基础上来理解十六进制,只需要转换一些概念就行。
十六进制是使用16个“数字”来表示的,由于0~9只有10个数字,因此就制定了A、B、C、D、E、F六个字母来表示剩余的几个数字,分别是10、11、12、13、14、15。
简单来说,就是把四个二进制“数字”为一组,合起来用一个“十六进制”里的“数字”来表达。
二进制的表达使用的是0b,十六进制的表达使用的是0x
我们按照前边的十进制和二进制的方式来举例几个十六进制的数值:
上面表格的表达方式用数学运算表达式表示的话,就是下面这样:
请留意:0xA等于10,0xF等于15
在二进制和十进制中,相邻进制之间相差的倍数分别为2倍和10倍,而在十六进制中从上表可以看出相邻进制之间相差倍数为16倍,也就是:
“逢十六进一”,采用“1/16/256/4096/......”的进位递增。
1.4补充信息
针对上面的描述,补充一些信息:
- 书写二进制数值时,为了工整一般会补足四位
例如:“0b110”一般写作“0b0110”
- 书写十六进制数值时,为了工整一般会补足两位或者四位
例如:0x06(补足为两位),0x01FF(补足为四位)
2.Java中实现二进制、十进制、十六进制转换
Java中的Integer类提供了将int转为二进制、八进制、十进制、十六进制的方法,分别是:
Form | To | Method |
十进制 | 十六进制 | Integer.toHexString(int i) |
十进制 | 八进制 | Integer.toOctalString(int i) |
十进制 | 二进制 | Integer.toBinaryString(int i) |
十六进制 | 十进制 | Integer.valueOf("FFFF",16).toString() |
八进制 | 十进制 | Integer.valueOf("876",8).toString() |
二进制 | 十进制 | Integer.valueOf("0101",2).toString() |
通过Integer.parseInt()方法可直接将二进制、八进制、十六进制转为十进制
parseInt(String s, int radix)
使用第二个参数指定的基数,将字符串参数解析为有符号的整数。
3.Java &、&&、|、||、^、<<、>>、~、>>>等运算符
java运算大致分为逻辑运算符、算数运算符、位运算符和其他运算符:
- 逻辑运算符:&&、||、!
- 算数运算符:+、-、*、/、+=
- 位运算符:^、|、&
- 其他运算符:三元运算符
3.1.1逻辑与(&&)
&&逻辑与也称为短路逻辑与,先运算&&左边的表达式,一旦为假,后续不管多少表达式,均不再计算,一个为真,再计算右边的表达式,两个为真才为真
举例:
if(a == 0 && b==1)
3.1.2逻辑或(||)
逻辑或||的运算规则是一个为真即为真,后续不再计算,一个为假再计算右边的表达式。
举例:
if(a==0 || b==0)
3.1.3逻辑非(!)
即表示不等于
举例:
if(a != 0)
3.1.4按位与(&)
&按位与的运算规则是将两边的数转换为二进制位,然后运算最终值,运算规则即(两个为真才为真)1&1=1 , 1&0=0 , 0&1=0 , 0&0=0
举例:
int i = 3 & 5;
3的二进制位是0000 0011 , 5的二进制位是0000 0101 , 那么就是011 & 101,由按位与运算规则得知,001 & 101等于0000 0001,最终值为1
3.1.5按位或(|)
|按位或和&按位与计算方式都是转换二进制再计算,不同的是运算规则(一个为真即为真)1|0 = 1 , 1|1 = 1 , 0|0 = 0 , 0|1 = 1
举例:
int i = 6 | 2;
6的二进制位0000 0110 , 2的二进制位0000 0010 , 110|010为110,最终值0000 0110,故6|2等于6
3.1.6异或运算符(^)
^异或运算符顾名思义,异就是不同,其运算规则为1^0 = 1 , 1^1 = 0 , 0^1 = 1 , 0^0 = 0
举例:
int i = 5 ^ 9;
5的二进制位是0000 0101 , 9的二进制位是0000 1001,也就是0101 ^ 1001,结果为1100 , 00001100的十进制位是12
3.1.7左移运算符(<<)
凡位运算符都是把值先转换成二进制再进行后续的处理.
举例:
int i = 5 << 2;
5<<2的意思为5的二进制位往左挪两位,右边补0,5的二进制位是0000 0101 , 就是把有效值101往左挪两位就是0001 0100 ,正数左边第一位补0,负数补1,等于乘于2的n次方,十进制位是20
3.1.8右移运算符(>>)
凡位运算符都是把值先转换成二进制再进行后续的处理.
举例:
int i = 5 >> 2;
5的二进制位是0000 0101,右移两位就是把101左移后为0000 0001,正数左边第一位补0,负数补1,等于除于2的n次方,结果为1
3.1.9取反运算符(~)
取反就是1为0,0为1
规律:正整数N取反结果为:负(N+1);负整数-N取反结果为:N-1
举例:
int i = ~5;
5的二进制位是0000 0101,取反后为1111 1010,值为-6
3.1.10无符号右移运算符(>>>)
正数无符号右移
无符号右移运算符和右移运算符的主要区别在于负数的计算,因为无符号右移是高位补0,移多少位补多少个0。
举例:
int i = 15 >>> 2;
15的二进制位是0000 1111 , 右移2位0000 0011,结果为3
负数无符号右移
举例:
long i = -6 >>> 3;
-6的二进制是6的二进制取反再加1,6的二进制也就是0000 0000 0000 0000 0000 0000 0000 0110,取反后加1为1111 1111 1111 1111 1111 1111 1111 1010,右移三位0001 1111 1111 1111 1111 1111 1111 1111
4.int & 0xFF的含义
在将byte字节转为Hex十六进制时,会使用 & 0xFF将字节值处理一下,如下图所示:
原因:
Java基础数据类型长度:
byte=1个字节=8位二进制
计算机存储数据机制:正数存储的二进制原码,负数存储的是二进制的补码。 补码是负数的绝对值反码加1。
对于正数(00000001)原码来说,首位表示符号位,反码 补码都是本身
对于负数(100000001)原码来说,反码是对原码除了符号位之外作取反运算即(111111110),补码是对反码作+1运算即(111111111)
举例:
byte[] b = new byte[5];
b[0] = -12;
而-12 的绝对值原码是:0000 1100 取反: 1111 0011 加1: 1111 0100
byte --> int 就是由8位变 32 位 高24位全部补1: 1111 1111 1111 1111 1111 1111 1111 0100 ;
0xFF的二进制表示就是:1111 1111。 高24位补0:0000 0000 0000 0000 0000 0000 1111 1111;
-12的补码与0xFF 进行与(&)操作 最后就是0000 0000 0000 0000 0000 0000 1111 0100
byte类型的数字要&0xff再赋值给int类型,其本质原因就是想保持二进制补码的一致性。
当byte要转化为int的时候,高的24位必然会补1,这样,其二进制补码其实已经不一致了,&0xff可以将高的24位置为0,低8位保持原样。这样做的目的就是为了保证二进制数据的一致性。
有人问为什么上面的式子中b[0]不是8位而是32位,因为当系统检测到byte可能会转化成int或者说byte与int类型进行运算的时候,就会将byte的内存空间高位补1(也就是按符号位补位)扩充到32位,再参与运算。
5.为什么java中中文转byte字节数组出现负数
GBK采用双字节8位表示dao,总体编码范du围为 8140 -- FEFE,首字节在zhi 81 -- FE 之间,尾字dao节在 40 -- FE 之间。
ASCII是7位编码内,只使用前7位,第容8位补0,所以转换成整数始终为正数,而GBK是8位编码,也就是说一个字节中的第8位可以为1,如1010 1101,而将其转换成byte类型时,byte值为10101101,以补码存储,第8位被当成符号位,当然是负数了,值为:-83。
“何”字的GBK编码是:BA CE(1011 1010 1100 1110),两个字节第8位都为1,对byte类型来说,都被理解为最高位符号位。这样值就变成-70和-50了。