Java编程基础--运算符/原码反码补码【知识体系构建系列】

运算符一览

算术运算符

+ 加法 - 相加运算符两侧的值
- 减法 - 左操作数减去右操作数
* 乘法 - 相乘操作符两侧的值
/ 除法 - 左操作数除以右操作数
% 取模 - 右操作数除左操作数的余数
++ 自增 - 操作数的值增加1
– 自减 - 操作数的值减少1

关系运算符

== 检查如果两个操作数的值是否相等,如果相等则条件为真
!= 检查如果两个操作数的值是否相等,如果值不相等则条件为真
> 检查左操作数的值是否大于右操作数的值,如果是那么条件为真
< 检查左操作数的值是否小于右操作数的值,如果是那么条件为真
>= 检查左操作数的值是否大于或等于右操作数的值,如果是那么条件为真
<= 检查左操作数的值是否小于或等于右操作数的值,如果是那么条件为真

位运算符

& 按位与操作符,当且仅当两个操作数的某一位都非0时候结果的该位才为1
| 按位或操作符,只要两个操作数的某一位有一个非0时候结果的该位就为1
^ 按位异或操作符,两个操作数的某一位不相同时候结果的该位就为1
~ 按位补运算符翻转操作数的每一位
<< 按位左移运算符。左操作数按位左移右操作数指定的位数
>> 按位右移运算符。左操作数按位右移右操作数指定的位数
>>> 按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充

逻辑运算符

&& 称为逻辑与运算符。当且仅当两个操作数都为真,条件才为真
| | 称为逻辑或操作符。如果任何两个操作数任何一个为真,条件为真
! 称为逻辑非运算符。用来反转操作数的逻辑状态。如果条件为true,则逻辑非运算符将得到false。

赋值运算符

= 简单的赋值运算符,将右操作数的值赋给左侧操作数
+= 加和赋值操作符,它把左操作数和右操作数相加赋值给左操作数
-= 减和赋值操作符,它把左操作数和右操作数相减赋值给左操作数
*= 乘和赋值操作符,它把左操作数和右操作数相乘赋值给左操作数
/= 除和赋值操作符,它把左操作数和右操作数相除赋值给左操作数
%= 取模和赋值操作符,它把左操作数和右操作数取模后赋值给左操作数
<<= 左移位赋值运算符
>>= 右移位赋值运算符
&= 按位与赋值运算符
^= 按位异或赋值操作符
|= 按位或赋值操作符

其它运算符

条件运算符(?:)
instanceOf

原码反码补码

各自的概念

原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值. 比如如果是8位二进制:

[+1]原 = 0000 0001
[-1]原 = 1000 0001

第一位是符号位. 因为第一位是符号位, 所以8位二进制数的取值范围就是:
[1111 1111 , 0111 1111] 即[-127 , 127]
原码是人脑最容易理解和计算的表示方式.

反码的表示方法是:
正数的反码是其本身
负数的反码是在其原码的基础上, 符号位不变,其余各个位取反.

[+1] = [00000001]原 = [00000001]反
[-1] = [10000001]原 = [11111110]反

可见如果一个反码表示的是负数, 人脑无法直观的看出来它的数值. 通常要将其转换成原码再计算.

补码的表示方法是:
正数的补码就是其本身
负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)

[+1] = [00000001]原 = [00000001]反 = [00000001]补
[-1] = [10000001]原 = [11111110]反 = [11111111]补

对于负数, 补码表示方式也是人脑无法直观看出其数值的. 通常也需要转换成原码在计算其数值.

为什么计算机用补码来计算?

对于正数来说:[+1] = [00000001]原 = [00000001]反 = [00000001]补
对于负数来说:[-1] = [10000001]原 = [11111110]反 = [11111111]补

计算机辨别”符号位”显然会让计算机的基础电路设计变得十分复杂! 于是人们想出了将符号位也参与运算的方法. 我们知道, 根据运算法则减去一个正数等于加上一个负数, 即: 1-1 = 1 + (-1) = 0 , 所以机器可以只有加法而没有减法, 这样计算机运算的设计就更简单了.

原码:1-1=0,1 - 1 = 1 + (-1) = [00000001]原 + [10000001]原 = [10000010]原 = -2,如果用原码且让符号位加入计算,结果是不正确的

反码:1-1=0,1 - 1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原= [0000 0001]反 + [1111 1110]反 = [1111 1111]反 = [1000 0000]原 = -0, 发现用反码计算减法, 结果的真值部分是正确的. 而唯一的问题其实就出现在”0”这个特殊的数值上. 虽然人们理解上+0和-0是一样的, 但是0带符号是没有任何意义的. 而且会有[0000 0000]原和[1000 0000]原两个编码表示0.

补码,补码的出现, 解决了0的符号以及两个编码的问题:1-1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [0000 0001]补 + [1111 1111]补 = [0000 0000]补=[0000 0000]原
使用补码, 不仅仅修复了0的符号以及存在两个编码的问题, 而且还能够多表示一个最低数. 这就是为什么8位二进制, 使用原码或反码表示的范围为[-127, +127], 而使用补码表示的范围为[-128, 127].

因为机器使用补码, 所以对于编程中常用到的32位int类型,比如Java, 可以表示范围是: [-2^31, 2^31-1] 因为第一位表示的是符号位.而使用补码表示时又可以多保存一个最小值.

题目

请写出下面几个表达式的结果,答案可以用10进制或16进制书写
1. 0xaa | 0x55
2. 15 & 240
3. 10 ^ 12
4. -2 >> 1
5. -2 >>> 1

解答:

  1. 分析:十六进制数用0x……来表示,后面一个十六进制位是四位,两个十六进制位为一个字节,最多后面可以有8个十六进制位,32个字节,如:0xFFFFFFFF。 或(“ | ”)运算,全0为0,其他为1。
    所以:0xaa 用二进制表示为 10101010 ,0x55 用二进制表示为 01010101 ,按位或之后为 11111111 ,十进制数为255,十六进制数为 0xFF 。

  2. 分析:10进制转换成2进制,用该数字除以2,记录商和余数,利用商再次除以2,记录商和余数……直到上为0或余数为0停止,余数逆序组成二进制的从低到高位(最后的余数为二进制最低位)。与(“ & ”)运算,全1为1,其他为0 。
    所以: 15 等于1111 ,240等于 11110000,15前面用0补齐为00001111 ,按位与之后为 00000000 ,即结果为0

  3. 分析: 亦或(“ ^ ”)运算,相同取0,不同取1 。
    所以:1010 ^ 1100 =0110 , 十进制表示为6,十六进制表示为 0x06 。

  4. 分析: 带符号右移(“ >> ”),即有符号位时,负数符号位补1,正数符号位补0, -2 的二进制求法是正数取反加1,因此 2 的二进制表示为0000 0000 0000 0000 0000 0000 0000 0010 ,取反加一为
    1111 1111 1111 1111 1111 1111 1111 1110 ,即 -2 的二进制表示。
    注: >> , << , >>> , 运算符只针对int型和long型,byte ,short ,char型需要转换成Int型在进行操作。
    所以: 带符号右移之后为 1111 1111 1111 1111 1111 1111 1111 1111 ,除符号位之外,减一取反,得到带符号十进 制数为 -1 。

  5. 分析:无符号右移 (“ >>> ”) ,即无论正负数,右移之后符号位均补 0 。
    所以: -2 的二进制无符号右移一位之后为 0111 1111 1111 1111 1111 1111 1111 1111,即 2^31 - 1,二的三十一次方减一。
    注:右移和无符号右移主要区别就在于左面最高位补 0 还是补 1 的问题,无符号右移任何时候最高位都补 0 , 有符号右移则是正数补 0 ,负数补 1 。(没有无符号左移!)

你可能感兴趣的:(java)