公司要转Java了,一开始心里还有点抵触的,那段时间玩英雄杀,顿时想到了鹅兵已掠地,四方Java声
的悲凉处地(来了两个鹅厂大佬)。毕竟C#语法非常优雅,特别是异步编程,爽的一批,其实也就私下写代码爽爽,因为这公司根本没用异步编程!毕竟不是每个人都能真正明白异步在服务端开发的作用。现在也因为一些事想通了,Java还是有很多优势的,多学点东西总没坏处,顺便在学Java中记录下曾经的一些知识点,好记性不如烂笔头。
符号位
计算机中用二进制中的最高位(最左边)来表示这个数是负数还是正数,也就是符号位,其中0表示正数,1表示负数
。当然,也有没有符号位的数,他们的最高位表示的是数字本身的值。故可以把二进制数分为有符号数(signed
,ps:这也就是C#中sbyte
中s
的含义)和无符号数(unsigned
)
比如:10001000
在8位有符号数
中表示的是为一个负数,在8位无符号
数中表示136
有些编程语言,比如Java
,它所有和数字相关的数据类型都是有符号的;而有些编程语言,比如C、C#
,他有诸如 unsigned int
这种无符号位的数据类型。
溢出
在数学中,数字可以有无穷大,无穷小。可是,在计算机中,数字都是有长度的;无论是何种数据类型,都有一个上限和下限。
对于n
位的整数类型,符号位是1
,其他位都是0
,就是该类型是最小值-(2^(n-1))
,注意不是0
,所有位都是0
(符号位也是0
)时才为0
;符号位是0
,其他位都是1
,就是该类型的最大值2^(n-1)-1
。一旦某个数字超过了这个范围,就会发生溢出
。如果小于最小值,叫下溢出
。如果大于最大值,叫上溢出
。
在Java
和C#
中,int类型是32位,表示-2^31 ~ 2^31 - 1
之间的任意整数(最高位是符号位
),表达式(2^31 - 1) + 1
就超过了int
能表达的最大整数,那么这个表达式的结果是什么呢?答案是:-2^31
,这就是溢出
// 2^31-1 = 2147483647,-2^31= -2147483648
System.out.println(2147483647+1); // -2147483648
System.out.println(-2147483648-1); // 2147483647
var num = int.MaxValue;
Console.WriteLine(num + 1 == int.MinValue); // True
try
{
// unchecked 禁止溢出检测,默认是此规则
Console.WriteLine(unchecked(num + 1 == int.MinValue));// True
// checked 对整型运算做溢出检测
Console.WriteLine(checked(num + 1 == int.MinValue));
}
catch (Exception ex)
{
Console.WriteLine(ex); // System.OverflowException: 算术运算导致溢出。
}
原码
原码
就是我们看到的二进制的原始表示。下文案例中数字均以int类型为例,不再复述!
+8 = 0x00_00_00_08[原] = 00000000_00000000_00000000_00001000[原]
-8 = 0x80_00_00_08[原] = 10000000_00000000_00000000_00001000[原]
可以看到负数的原码就是把它的相反数
的原码的符号位改为 1
反码
正数的反码就是其本身,负数的反码就是除了符号位外其他位取反取反就是0变1
1变0
+8 = 0x00_00_00_08[原] = 0x00_00_00_08[反] = 00000000_00000000_00000000_00001000[反]
-8 = 0x80_00_00_08[原] = 0xFF_FF_FF_F7[反] = 11111111_11111111_11111111_11110111[反]
补码
正数的补码就是其本身,负数的补码 = 其反码 + 1 计算机中数字就是以补码的存储的。扩展1,扩展2
+8 = 0x00_00_00_08[原] = 0x00_00_00_08[反] = 0x00_00_00_08[补] = 00000000_00000000_00000000_00001000[补]
-8 = 0x80_00_00_08[原] = 0xFF_FF_FF_F7[反] = 0xFF_FF_FF_F8[补] = 11111111_11111111_11111111_11111000[补]
逻辑运算符
位与 &(And)
两个操作数进行按位与操作,仅当两个操作位都为1
时结果位才为1
。例:12&7=4
,12&8=8
位或 |(Or)
两个操作数进行按位或操作,只要任意操作位为1
时结果位就为1
。例:12|7=15
,12|8=12
位异或 ^(Xor)
两个操作数进行按位异或操作,仅当两个操作位中有一个为1
时结果位才为1
。例:12^7=11
,12^8=4
位非 ~(Not)
对操作数的每个位都取反,这是一个一元操作符。例:~12= ?
。我们知道计算机中数字以补码的形式存储,而正数的原码=补码=0...00001100
,取反后的结果是1...11110011
,这是负数的补码形式,我们直接看不出来他的值是多少,得转换成负数原码。我们知道负数原码转补码的过程是 原码=>(除符号位全部取反)=>反码=>+1=>补码
,负数补码转原码就是把这个过程反过来,即补码=>-1=>反码=>除符号位全部取反=>原码
。有了这个公式,可以算出反码为1...11110010
,然后转为原码为1...00001101
,最后转10进制为-1*2^3+2^2+2^0 = -13
,不要忘了符号位是1
(),流程如下:~12=00000000_00000000_00000000_00001100[补]=>取反=>11111111_11111111_11111111_11110011[补]=>11111111_11111111_11111111_11110010[反]=>10000000_00000000_00000000_00001101[原]=>-13
ps:对正负数取反有个简单的公式:~x=-(x+1)
,~12=-(12+1)
,~-13=-(-13+1)
移位运算符
逻辑移位
逻辑移位分逻辑左移和逻辑右移,移动中的空位补0。Java中有一个无符号
右移(>>>
),就是指的逻辑右移。C#中没有逻辑右移。Java和C#中都没有逻辑左移,因为逻辑左移的规则同算术左移。
算术移位
算术移位分算术左移和算术右移,算术左移同逻辑左移,逻辑右移和算术右移的区别是,逻辑右移左侧空位和符号位补0,算术右移符号位不变,左侧空位补符号位的值。Java和c#中都有这两种算术移位。12 >> 3
表示的含义就算术右移,数字12向右移3位。3 << 2
指的是算术左移,数字3向左移2位。这里有个特点,箭头>>
指向右边就是右移,箭头<<
指向左边就是左移,箭头右边的数字是移动的位数
System.out.println(12 >> 3);//0...00001100 >> 0...00000001=1
System.out.println(12 >> 4);//0...00001100 >> 0...00000000=0
System.out.println(12 >> 5);//0...00001100 >> 0...00000000=0
//1...00001100[原]=>1...11110011[反]=>1...11110100[补] >> 1...11111110[补]=>1...11111101[反]=>1...00000010[原]
System.out.println(-12 >> 3);//-2
System.out.println(3 << 2);//0...00000011 >> 0...00001100=12
System.out.println(3 << 3);//0...00000011 >> 0...00011000=24
/*
-7的原码为1000...00000111,反码为:1111...11111000,补码为:1111...11111001
右移3位后变为:00011111_11111111_11111111_11111111 转10进制为:536870911
用十六进制表示下流程:80_00_00_07[原]=>FF_FF_FF_F8[反]=>FF_FF_FF_F9[补]=>右移3位=>
1F_FF_FF_FF[补]=>正数的 原码=反码=补码=>1F_FF_FF_FF[原]=>转10进制为:536870911
*/
System.out.println(-7 >>> 3);//536870911
Console.WriteLine(12 >> 3);//0...00001100 >> 0...00000001=1
Console.WriteLine(12 >> 4);//0...00001100 >> 0...00000000=0
Console.WriteLine(12 >> 5);//0...00001100 >> 0...00000000=0
//1...00001100[原]=>1...11110011[反]=>1...11110100[补] >> 1...11111110[补]=>1...11111101[反]=>1...00000010[原]
Console.WriteLine(-12 >> 3);//-2
Console.WriteLine(3 << 2);//0...00000011 >> 0...00001100=12
Console.WriteLine(3 << 3);//0...00000011 >> 0...00011000=24