您的位置:首页 > 理论基础

Java中基本成员默认值+计算机中负数的表示+按位操作符和移位操作符

2016-09-14 17:00 330 查看

1.基本成员默认值

若类的某个成员是基本数据类型,即使没有进行初始化,Java也会确保它获得一个默认值,见下表:

基本类型默认值
booleanfalse
char‘\u0000’(null)
byte(byte)0
short(short)0
int0
long0L
float0.0f
double0.0d
只有当变量作为类的成员变量使用时,Java才确保给定其默认值,以确保那些是基本类型的成员变量得到初始化。但这些变量对于我们的程序来说可能是不正确的,所以最好由我们自己初始化。

然而上述确保初始化的方法并不适用于“局部”变量,即某个方法中的局部变量。

验证程序:

public class Practice1 {

int int_a ;
char char_b;
byte byte_c;
short short_d;
boolean boolean_e;
long long_f;
float float_g;
double double_h;

public static void main(String[] args) {
Practice1 p1 = new Practice1();
System.out.println(p1.int_a);
System.out.println(p1.char_b);
System.out.println(p1.byte_c);
System.out.println(p1.short_d);
System.out.println(p1.boolean_e);
System.out.println(p1.long_f);
System.out.println(p1.float_g);
System.out.println(p1.double_h);
}
}


输出结果:



2.计算机中负数的表示

计算机中的数均放在寄存器中,通常称寄存器的位数为机器字长。比如我们现在通常所说的的64位机,指的就是通用寄存器的位宽是64bit。所谓无符号数,即没有符号的数,在寄存器中的每一位都可用来存放数值。当存放有符号数时,则需要留出位置来存放符号。因此,在机器字长相同时,无符号数和有符号数所对应的数值范围是不同的。

对于有符号数在计算机中的表示,要明确以下几个编码方式:

(1)原码:将一个整数,转换成二进制,就是其原码,其中最高位为符号位,0表示正数,1表示负数。比如,我们用8位二进制表示一个数,+11的原码为00001011,而-11的原码是10001011.

原码不能直接参加运算,可能会出错,比如,在数学上,1+(-1)=0,而在二进制中,000000001+10000001 = 10000010,换算成十进制为-2,显然结果出错了。所以原码的符号位不能直接参与运算,必须和其他位分开,这就增加了硬件的开销和复杂性。

(2)反码:反码通常用来作为原码求补码或者由补码求原码的中间过渡。正数的反码还是其原码;而负数的反码就是将该负数的原码除了符号位之外的位按位取反,得到的码即为反码。比如,00001010的反码还是00001010,而10001010的反码就是11110101.

(3)补码:正数的补码还是其本身。负数的补码是该负数的反码再加1.比如,00001010的补码还是00001010,而10001010的补码就是11110101 + 1 = 11110110。

【另外,由负数的补码求原码的步骤,也是先求反码,再加1,步骤是一样的】

引入补码的概念,就是为了消除减法的概念。

还举最初的例子,1+(-1)=0,现在用补码进行运算,1的补码为其原码本身00000001,-1的补码为11111111,所以00000001+11111111 = 1,0000,0000,最高位1溢出,自动去除,得到的结果为00000000,即数字0的补码,答案正确。

再举一个例子,8+(-12) = -4,8的补码就是00001000,-12的补码为11110011+1=11110100,00001000+11110100 = 11111100,这个补码表示什么数呢,让我们将得到的补码转换成原码,即为10000011+1=10000100,这个数即为十进制的-4,答案正确。

【总结】

1.计算机中的有符号数是用补码表示的,补码的引入也消除了计算机中的减法运算,简化了电路

2.补码的引入同时扩大了负数可以表示的范围,比如如果只采用原码表示有符号数,那么对于8位机,正数可以表示的范围就是[+0,+127],原码表示就是[00000000,01111111];而负数可以表示的范围就是[-127,-0],原码表示就是[11111111,1000000],这样有什么缺点呢,这样我们8个数位原本可以表示2^8=256个数,但是我们却只能表示255个,因为我们有+0,-0两个0。

统一采用补码之后,我们可以用补码表示出-128,-128的补码就是10000000,但是它的原码表示不出来。我们用128的补码参与运算得到的答案都是对的,不信可以验证。这样8位机的有符号数的范围就变成了[-128,127],扩大了范围。

所以,这就能够理解Java中基本数字类型的表示范围了:

基本类型大小最小值最大值包装器类型
byte8 bits-128+127Byte
short16 bits-2^15+2^15-1Short
int32 bits-2^31+2^31-1Integer
long64 bits-2^63+2^63-1Long
进一步的验证,在Java中,我们对于int型的变量,可以打印出它的二进制表示,如下:

System.out.println(Integer.toBinaryString(-8));
System.out.println(Integer.toBinaryString(8));


输出结果为:



可以看到,输出的确实是补码形式(4B,也即32bit)

3.按位操作符

按位操作符用来操作整数基本数据类型中的单个’bit’,即二进制位。按位操作符会对两个参数中对应的位执行布尔代数运算。

(1)按位与
&
:如果两个输入位都是1,则按位“与”生成一个输出位1,否则生成一个输出位0

(2)按位或
|
:如果两个输入位里只要有一个是1,则生成一个输出位1.

(3)异或
^
:两个输入位不同,则输出1,否则输出0

(4)非(取反)
~
:按位取反,即0变成1,1变成0

按位操作符本身很简单,但需要注意的是所有的操作都是包括符号位的,要知道Java中所有的数值类型都是有符号数值(即最高位为符号位),而且是用补码表示的。

比如,8^-8是多少呢?

这个问题,我们不可能一下子知道答案,要实际计算,int是4个字节,在这里为了书写方便,我们假设是2个字节,因为2个字节足够表示8了。

8的二进制补码表示为:00001000,

-8
的二进制补码为:10001000求反码再加1,即为11110111 + 1 = 11111000,

所以00001000 ^ 11111000 = 11110000,那么这个数是几呢?对这个数再求原码就知道了:

10001111 + 1 = 10010000,即为-16.

所以:
8^-8 = -16


再比如,~-8是多少?这个比较简单,-8的二进制补码表示为11111000,按位取反即为00000111,即为7.

~-8 = 7


上述结果用程序验证都是正确的,其实也就2个要点:

1.Java中的数值都是补码表示

2.按位操作符都是包括符号位一起操作

注:按位操作符可与等号联合使用,&=、|=和^=都是合法的

4.移位操作符

移位操作符的运算对象也是二进制的位,并且它只能用来处理整数类型。

(1)左移操作符
<<
,按照操作符右侧指定的位数将操作符左边的操作数向左移动(在低位补0)

(2)有符号右移操作符
>>
,按照操作符右侧指定的位数将操作符左边的操作数向右移动。如果符号为正,则在高位补0;如果符号为负,则在高位补1。

(3)无符号右移操作符
>>>
,它使用0扩展,无论正负,都在高位插入0.

掌握了上述两个要点,移位操作符也是相当简单,但是要注意右移操作符的有符号和无符号处理,要知道对于一个负数,如果是有符号右移,则它还是一个负数,但是如果是无符号右移,则会变成正数。

另外,对char、byte或者short类型的数值进行移位处理,那么在移位之前,它们会被转换为int类型,并且得到的结果也是一个int类型。

最重要的一点,Java中的移位操作是循环移位,这也是我在测试的时候偶然发现的,比如
1<<3 = 8
,我们知道int有4个字节,也就是32位,那么如果1向左移的位数超过32位怎么办呢?测试发现
1 << 35 = 8
,可见它是循环移位,所以向左移3位和向左移35位是一样的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: