Java中的位操作符
&(与)操作
两个操作数中位都为1,结果才为1,否则结果为0
public class data13
{
public static void main(String[] args)
{
int a=129;
int b=128;
System.out.println("a 和b 与的结果是:"+(a&b));
}
}
运行结果:
a 和b 与的结果是:128
下面分析这个程序:
“a”的值是129,转换成二进制就是10000001,而“b”的值是128,转换成二进制就是10000000。根据与运算符的运算规律,只有两个位都是1,结果才是1,可以知道结果就是10000000,即128。
|(或)运算符
或运算符用符号“|”表示,其运算规律如下:
两个位只要有一个为1,那么结果就是1,否则就为0,下面看一个简单的例子。
public class data14
{
public static void main(String[] args)
{
int a=129;
int b=128;
System.out.println("a 和b 或的结果是:"+(a|b));
}
}
运行结果
a 和b 或的结果是:129
下面分析这个程序段:
a 的值是129,转换成二进制就是10000001,而b 的值是128,转换成二进制就是10000000,根据或运算符的运算规律,只有两个位有一个是1,结果才是1,可以知道结果就是10000001,即129。
^(异或)运算符
异或运算符是用符号“^”表示的,其运算规律是:
两个操作数的位中,相同则结果为0,不同则结果为1。下面看一个简单的例子。
public class data16
{
public static void main(String[] args)
{
int a=15;
int b=2;
System.out.println("a 与 b 异或的结果是:"+(a^b));
}
}
运行结果
a 与 b 异或的结果是:13
分析上面的程序段:a 的值是15,转换成二进制为1111,而b 的值是2,转换成二进制为0010,根据异或的运算规律,可以得出其结果为1101 即13。
~(非)运算符
非运算符用符号“~”表示,其运算规律如下:
如果位为0,结果是1,如果位为1,结果是0,下面看一个简单例子。
public class data15
{
public static void main(String[] args)
{
int a=2;
System.out.println("a 非的结果是:"+(~a));
}
}
运行结果是: -3, 2的二进制表示是:0000….000010
然后取~, 结果是1111….111101, 将补码转换成十进制就是-3
<< (左移)运算符
<< 和>>为数值位移,>>>为逻辑位移。【注】:Java中不存在<<<。
m<<n
的含义:把整数m表示的二进制数左移n位,高位移出n位都舍弃,低位补0. (此时将会出现正数变成负数的形式)
3<<2
剖析:
3的二进制形式: 00000000 00000000 00000000 00000011,按照左移的原理,得到00000000 00000000 00000000 00001100,即为12.
左移使整数变为负数:
10737418<<8
10737418二进制表示形式:00000000 10100011 11010111 00001010,按照左移的原理,得到10100011 11010111 00001010 00000000,即为:-1546188288.
>>(逻辑右移)运算符
m>>n的含义:把整数m表示的二进制数右移n位,m为正数,高位全部补0;m为负数,高位全部补1.
>> 会根据符号位的正负进行补位
3>>2剖析:
3二进制形式: 00000000 00000000 00000000 00000011,按照右移的原理,得到00000000 00000000 00000000 00000000,即为0.
-3>>2剖析:
-3二进制形式: 11111111 11111111 11111111 11111101,按照右移的原理,得到11111111 11111111 11111111 11111111,即为-1.
以上:每 个整数表示的二进制都是32位的,如果右移32位和右移0位的效果是一样的。依次类推,右移32的倍数位都一样。
>>>(无符号右移)运算符
m>>>n:整数m表示的二进制右移n位,不论正负数,高位都补零。
3>>>2剖析:
3二进制形式: 00000000 00000000 00000000 00000011,按照无符号右移的原理,得到00000000 00000000 00000000 00000000,即为0.
-3>>>2剖析:
-3二进制形式: 11111111 11111111 11111111 11111101,按照无符号右移的原理,得到00111111 11111111 11111111 11111111,即为1073741823.
&0xFF的意义
byte类型转换成int类型时需要进行 &0xFF的操作, 原因跟计算机的存储原理有关。
数据在内存是以补码的形式存在的。
补码:
正数(000000001): 正数的补码和反码都是它本身。
负数(100000001): 反码是对原码除了符号位之外作取反运算即(111111110),补码是对反码作+1运算即(111111111)。
System.out.println(-127 & 0xFF);
// 结果是129
下面分析-127&0xFF的过程
-127的补码表示(10000001)
JVM向控制台输出时,检测到byte向int类型的转换于是做了一个补位处理
(10000001) -> (11111111,11111111,11111111,10000001)
这个32位的补码表示也是-127, 此时转换过后的二进制数据的十进制表示仍然是相同的,但是此时补码的因为高位补1已经不一致了,我们为了保证补码的一致性,必须进行&0xFF操作.进行&0xFF操作
&0xff可以将高的24位置为0,低8位保持原样。这样做的目的就是为了保证二进制数据的一致性。
当然,保证了二进制数据性的同时,如果二进制被当作byte和int来解读,其10进制的值必然是不同的,因为符号位位置已经发生了变化。
比如-127&0xff; -127 & 0xff=(11111111,11111111,111111111,10000001) & (00000000,00000000,00000000,11111111) = (00000000,00000000,00000000,10000001) 这个值算一下就是129.
// & 0xFF 的区别
System.out.println(Integer.toBinaryString(-127));
System.out.println(Integer.toBinaryString(-127 & 0xFF));
// 11111111111111111111111110000001
// 10000001
Java实现int和long类型的序列化
简单对上面的知识进行一个总结
这个小demo就是将int和long类型的数值写到文件中,然后在读出来,如果结果正确证明序列化成功.
创建文件
private static void createFile() {
sFile = new File("./", "obj");
sFile.delete();
if (!sFile.exists()) {
try {
sFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
int类型的序列化
int类型占8个字节,每次右移8个单位存储,需要分四次写入
// 将int数据写到硬盘上
private static void writeInt(int num) throws IOException {
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(sFile));
bos.write((num >> 0) & 0xFF);
bos.write((num >> 8) & 0xFF);
bos.write((num >> 16) & 0xFF);
bos.write((num >> 24) & 0xFF);
bos.flush();
bos.close();
}
// 同样也是分四次读,每次获取8位数据,然后读四次
// 从文件中读取int类型的值
private static int readInt() throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sFile));
int num = 0;
num |= bis.read();
num |= bis.read() << 8;
num |= bis.read() << 16;
num |= bis.read() << 24;
bis.close();
return num;
}
long类型的序列化
// long类型在JVM运行时占用8个字节,也就是64位,所以我们需要分8次向文件中写入数据
private static void writeLong(long num) throws IOException {
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(sFile));
bos.write((int) ((num >> 0) & 0xFFL));
bos.write((int) ((num >> 8) & 0xFFL));
bos.write((int) ((num >> 16) & 0xFFL));
bos.write((int) ((num >> 24) & 0xFFL));
bos.write((int) ((num >> 32) & 0xFFL));
bos.write((int) ((num >> 40) & 0xFFL));
bos.write((int) ((num >> 48) & 0xFFL));
bos.write((int) ((num >> 56) & 0xFFL));
bos.flush();
bos.close();
}
// 同样, 分8次从文件中读取数据,记得 &0xFFL 转换成long类型
private static long readLong() throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sFile));
long num = 0L;
num |= (bis.read() & 0xFFL) << 0;
num |= (bis.read() & 0xFFL) << 8;
num |= (bis.read() & 0xFFL) << 16;
num |= (bis.read() & 0xFFL) << 24;
num |= (bis.read() & 0xFFL) << 32;
num |= (bis.read() & 0xFFL) << 40;
num |= (bis.read() & 0xFFL) << 48;
num |= (bis.read() & 0xFFL) << 56;
bis.close();
return num;
}
测试代码
private static void serialIntTest() throws IOException {
final int magic = -0x20170507;
System.out.println("序列化前: " + magic);
writeInt(magic);
System.out.println("序列化后: " + readInt());
}
private static void serialLongTest() throws IOException{
// 如果是long类型,那么数字结尾处必须加上L,否则会认为是int类型
final long magicLong = -0x2017050720170507L;
System.out.println("序列化前: " + magicLong);
writeLong(magicLong);
System.out.println("序列化后: " + readLong());
}
完整代码
/**
* Created by yangtianrui on 17-5-7.
*/
public class WriteByteToFile {
private static File sFile;
public static void main(String[] args) throws Exception {
createFile();
serialIntTest();
serialLongTest();
/*
* 结果:
* 序列化前: -538379527
* 序列化后: -538379527
* 序列化前: -2312322461839328519
* 序列化后: -2312322461839328519
*/
}
private static void serialIntTest() throws IOException {
final int magic = -0x20170507;
System.out.println("序列化前: " + magic);
writeInt(magic);
System.out.println("序列化后: " + readInt());
}
private static void serialLongTest() throws IOException{
// 如果是long类型,那么数字结尾处必须加上L,否则会认为是int类型
final long magicLong = -0x2017050720170507L;
System.out.println("序列化前: " + magicLong);
writeLong(magicLong);
System.out.println("序列化后: " + readLong());
}
private static void writeInt(int num) throws IOException {
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(sFile));
bos.write((num >> 0) & 0xFF);
bos.write((num >> 8) & 0xFF);
bos.write((num >> 16) & 0xFF);
bos.write((num >> 24) & 0xFF);
bos.flush();
bos.close();
}
private static int readInt() throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sFile));
int num = 0;
num |= bis.read();
num |= bis.read() << 8;
num |= bis.read() << 16;
num |= bis.read() << 24;
bis.close();
return num;
}
private static void writeLong(long num) throws IOException {
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(sFile));
bos.write((int) ((num >> 0) & 0xFFL));
bos.write((int) ((num >> 8) & 0xFFL));
bos.write((int) ((num >> 16) & 0xFFL));
bos.write((int) ((num >> 24) & 0xFFL));
bos.write((int) ((num >> 32) & 0xFFL));
bos.write((int) ((num >> 40) & 0xFFL));
bos.write((int) ((num >> 48) & 0xFFL));
bos.write((int) ((num >> 56) & 0xFFL));
bos.flush();
bos.close();
}
private static long readLong() throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sFile));
long num = 0L;
num |= (bis.read() & 0xFFL) << 0;
num |= (bis.read() & 0xFFL) << 8;
num |= (bis.read() & 0xFFL) << 16;
num |= (bis.read() & 0xFFL) << 24;
num |= (bis.read() & 0xFFL) << 32;
num |= (bis.read() & 0xFFL) << 40;
num |= (bis.read() & 0xFFL) << 48;
num |= (bis.read() & 0xFFL) << 56;
bis.close();
return num;
}
private static void createFile() {
sFile = new File("./", "obj");
sFile.delete();
if (!sFile.exists()) {
try {
sFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}