java虚拟机的指令字节长度、代表着某种特定操作含义的操作码(Opcode)以及跟随其后的零至多个代表此操作所需参数的操作数(Operands)所构成。虚拟机中许多指令并不包含操作数,只有一个操作码。
运算符
首先先熟悉下移位运算符:<<、>>、>>>
<<:左移,按二进制位数,高位向左移出舍弃,低位补零
>>:右移,按二进制位数,低位向右移出,高位补零
>>>:无符号右移,忽略符号位,空位补0,计算机中数字以补码存储,首位都是符号位
private void test(int num){
int a = num;
printInfo(num);//1010
num = num << 1;
printInfo(num);//10100->转10进制是4+16=20
System.out.println("------------------");
num = a >> 1;
printInfo(num);//0101->转10进制是1+4=5
}
/**
* 输出一个int的二进制数
* @param num
*/
private static void printInfo(int num){
System.out.println(Integer.toBinaryString(num));
}
按位运算符:针对两个整数对应位执行布尔代数运算
按位与&:对应两个位都为1,则输出1,否则输出0;
按位或|:两个对应位只要有一位输出1,则输出1,否则输出0;
按位异或^:两个操作数低位对齐,高位补零,如果两个对应位相同,则输出0,否则输出1;
按位非~:输入1,输出0;输入0,输出1;
大端与小端
大端存储:数据的低位保存在内存的高地址中,数据的高位保存在内存的低地址中;
小端存储:数据的高位保存在内存的高地址中,数据的低位保存在内存的低地址中;
//输出结果:0x12,0x34,所以我们的数据是大端存储模式
private void bigOrShortEndian() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.writeInt(0x1234);
byte[] b = baos.toByteArray();
for (int i=0;i<b.length;i++){
System.out.println(Integer.toHexString(b[i]));
}
}
看其中DataOutputStream.writeInt(int v)源码如下:
public final void writeInt(int v) throws IOException {
out.write((v >>> 24) & 0xFF);
out.write((v >>> 16) & 0xFF);
out.write((v >>> 8) & 0xFF);
out.write((v >>> 0) & 0xFF);
incCount(4);
}
为什么v分四段写入内存,并且&0xFF呢?
基本类型的存储大小如下
基本数据类型 | 字节 | 位 | 取值范围 |
byte | 1 | 8 | -128~127 |
short | 2 | 16 | -2^16~2^16-1 |
int | 4 | 32 | |
long | 8 | 64 | |
float | 4 | 32 | |
double | 8 | 64 | |
boolean |
另:char2字节,16位。
得知以上之后,我们知道int是4字节,每次存储一个字节,所以分四段;因系统目前大都采用大端存储模式,所以数据的高位存储在内存低地址中,v>>>24,无符号右移24位,即先存int v二进制的最高字节4bit的二进制值,以此类推。
比如:存int 3的值,计算机都是存数据的补码:
原码:00000000 00000000 00000000 00000011
反码:01111111 11111111 11111111 11111100
补码=反码+1:01111111 11111111 11111111 11111101
v>>>24,即先存01111111在低内存地址中,其次11111111、11111111最后高内存地址存11111101,取数时根据指针位置先取第一块内存地址,取4字节,再以补码按既定规则计算成原数据值。
那为什么要&0xFF呢?
0xFF16进制是255,转成二进制是11111111
&按位与运算符,两者为同为1时,为1,否则为0,所以没有改变原值的大小,作用仅为切割v补码的二进制,每次切割成一字节。