当读写二进制文件,或者要把非标准长度的整数与标准长度的整数互相转换时,就要用到大量的位操作,虽然看起来很简单,实际上里面却有很多细节很容易出错。
首先,Java有些标准跟C/C++是不同的:
1、Java采用高字节在前的方式读写数据,例如要把一个4字节的int数值写入文件时,它是按照从高字节到低字节的顺序写入的,读取的时候也是这样读出来。
而C/C++则采用平台相关的方式,在Windows平台采用低字节在前的方式,在Linux/Unix平台则采用高字节在前的方式。
如果Java要读取C/C++创建的二进制文件,就要注意这个问题,最好先搞清楚原来的文件是采用哪种方式创建的。网络通信也要注意。
2、Java没有无符号数,无论byte,short,int,long都是有符号整数,而C/C++有个unsigned关键字可以设置一个数值为无符号数。
3、Java的整数基本数据类型就是byte,short,int,long这几个,长度分别为1,2,4,8字节,C/C++可以用typedef定义各种数据类型。
第二,Java是采用补码来存放整数的。
有时候觉得补码的定义有些奇怪,实际上可以这样理解:
把一个整数从0一直往上加1,加到溢出就变成了负数的最小值,然后再继续加1,最后又能回到0,实际上就是一个轮回。
例如一个byte类型的整数,一共有8位,能表示256个数值,采用补码的话数值范围就是-128~127,表示方法如下:
0 0000 0000
1 0000 0001
.
.
126 0111 1110
127 0111 1111
-128 1000 0000
-127 1000 0001
.
.
-1 1111 1111
0 0000 0000
第三、不同长度的整数转换。
如果是从较短的数转成较长的数,很简单,如果是正数就在高字节补0,如果是负数就在高字节补1。
例如byte的127转为short的127:
byte:0111 1111
short:0000 0000 0111 0111
byte的-127转为short的-127
byte:1000 0001
short:1111 1111 1000 0001
如果是从较长的数转成较短的数,实际上就是把高位都截断了,所以转出来的数值可能完全不是一回事了。
例如short的256转为byte:
short:0000 0001 0000 0000
byte: 0000 0000
把256变成了0
short的-255转成byte:
short:1111 1111 0000 0001
byte:0000 0001
把-255变成了1
第四、位运算操作符及它们的优先级
Java的位运算操作符包括:~非,|按位或,&按位与,^按位异或,<<左移,>>右移,>>>右移左侧补0
各种运算符的优先级如下表所示:
根据该表可以看到,位运算操作符的优先级各有不同,分别为:
1、~
2、>> << >>>
3、&
4、^
5、|
另外需要特别注意的是,除了~,其他位运算操作的优先级都低于加减,所以要记得以下语句是返回32而不是7!
1<<2+3
还有就是&、^、|的优先级都是低于逻辑操作符的,因此下面的语句会编译出错,幸好Java不像C那样对所有大于1的值都认为是真,否则下面的语句也能编译通过,但可能与你的意图不太一样,可能调试半天才发现。
if(3&1>0)
如果记不清楚,还是按照你的意图加上括号最保险。
第五、字节数组与整数之间的转换
为了把一个整数存入文件,或者从文件中读取一个整数,需要经常在字节数组和整数之间转换,这个过程要用到大量的位运算。
首先需要记住的是,在参与所有运算前,Java都会把byte、short类型的值都转换成int,然后再对转换后的int进行操作。例如下面的语句会编译出错:
首先,Java有些标准跟C/C++是不同的:
1、Java采用高字节在前的方式读写数据,例如要把一个4字节的int数值写入文件时,它是按照从高字节到低字节的顺序写入的,读取的时候也是这样读出来。
而C/C++则采用平台相关的方式,在Windows平台采用低字节在前的方式,在Linux/Unix平台则采用高字节在前的方式。
如果Java要读取C/C++创建的二进制文件,就要注意这个问题,最好先搞清楚原来的文件是采用哪种方式创建的。网络通信也要注意。
2、Java没有无符号数,无论byte,short,int,long都是有符号整数,而C/C++有个unsigned关键字可以设置一个数值为无符号数。
3、Java的整数基本数据类型就是byte,short,int,long这几个,长度分别为1,2,4,8字节,C/C++可以用typedef定义各种数据类型。
第二,Java是采用补码来存放整数的。
有时候觉得补码的定义有些奇怪,实际上可以这样理解:
把一个整数从0一直往上加1,加到溢出就变成了负数的最小值,然后再继续加1,最后又能回到0,实际上就是一个轮回。
例如一个byte类型的整数,一共有8位,能表示256个数值,采用补码的话数值范围就是-128~127,表示方法如下:
0 0000 0000
1 0000 0001
.
.
126 0111 1110
127 0111 1111
-128 1000 0000
-127 1000 0001
.
.
-1 1111 1111
0 0000 0000
第三、不同长度的整数转换。
如果是从较短的数转成较长的数,很简单,如果是正数就在高字节补0,如果是负数就在高字节补1。
例如byte的127转为short的127:
byte:0111 1111
short:0000 0000 0111 0111
byte的-127转为short的-127
byte:1000 0001
short:1111 1111 1000 0001
如果是从较长的数转成较短的数,实际上就是把高位都截断了,所以转出来的数值可能完全不是一回事了。
例如short的256转为byte:
short:0000 0001 0000 0000
byte: 0000 0000
把256变成了0
short的-255转成byte:
short:1111 1111 0000 0001
byte:0000 0001
把-255变成了1
第四、位运算操作符及它们的优先级
Java的位运算操作符包括:~非,|按位或,&按位与,^按位异或,<<左移,>>右移,>>>右移左侧补0
各种运算符的优先级如下表所示:
优先级
|
运算符
|
结合性
|
1
|
() [] .
|
从左到右
|
2
|
! +(正) -(负) ~ ++ --
|
从右向左
|
3
|
* / %
|
从左向右
|
4
|
+(加) -(减)
|
从左向右
|
5
|
<< >> >>>
|
从左向右
|
6
|
< <= > >= instanceof
|
从左向右
|
7
|
== !=
|
从左向右
|
8
|
&(按位与)
|
从左向右
|
9
|
^
|
从左向右
|
10
|
|
|
从左向右
|
11
|
&&
|
从左向右
|
12
|
||
|
从左向右
|
13
|
?:
|
从右向左
|
14
|
= += -= *= /= %= &= |= ^= ~= <<= >>= >>>=
|
从右向左
|
1、~
2、>> << >>>
3、&
4、^
5、|
另外需要特别注意的是,除了~,其他位运算操作的优先级都低于加减,所以要记得以下语句是返回32而不是7!
1<<2+3
还有就是&、^、|的优先级都是低于逻辑操作符的,因此下面的语句会编译出错,幸好Java不像C那样对所有大于1的值都认为是真,否则下面的语句也能编译通过,但可能与你的意图不太一样,可能调试半天才发现。
if(3&1>0)
如果记不清楚,还是按照你的意图加上括号最保险。
第五、字节数组与整数之间的转换
为了把一个整数存入文件,或者从文件中读取一个整数,需要经常在字节数组和整数之间转换,这个过程要用到大量的位运算。
首先需要记住的是,在参与所有运算前,Java都会把byte、short类型的值都转换成int,然后再对转换后的int进行操作。例如下面的语句会编译出错: