细谈字节存放顺序问题:
字节序,又称端序,尾序,英文:Endianness。在计算机科学领域中,字节序是指存放多字节数据的字节(byte)的顺序,典型的情况是整数在内存中的存放方式和网络传输的传输顺序。Endianness有时候也可以用指位序(bit)。
一般而言,字节序指示了一个UCS-2字符的哪个字节存储在低地址。如果LSByte在MSByte的前面,即LSB为低地址,则该字节序是小端序;反之则是大端序。在网络编程中,字节序是一个必须被考虑的因素,因为不同的处理器体系可能采用不同的字节序。在多平台的代码编程中,字节序可能会导致难以察觉的bug。
处理器体系:
- x86,MOS Technology 6502,Z80,VAX,PDP-11等处理器为Little endian。
- Motorola 6800,Motorola 68000,PowerPC 970,System/370,SPARC(除V9外)等处理器为Big endian
- ARM, PowerPC (除PowerPC 970外), DEC Alpha, SPARC V9, MIPS, PA-RISC and IA64的字节序是可配置的。
网络序
网络传输一般采用大端序,也被称之为网络字节序,或网络序。IP协议中定义大端序为网络字节序。
伯克利socket API定义了一组转换函数,用于16和32bit整数在网络序和本机字节序之间的转换。htonl,htons用于本机序转换到网络序;ntohl,ntohs用于网络序转换到本机序。
--------------------------------------------------------------------------------------------------------------------------------------------------
代码段一:
ByteBuffer buf = ByteBuffer.wrap(new byte[10]);
buf.asCharBuffer().put("abcde");
System.out.println(Arrays.toString(buf.array()));
System.out.println("----------------------------------------------");
buf.rewind();
buf.order(ByteOrder.BIG_ENDIAN);
buf.asCharBuffer().put("abcde");
System.out.println(Arrays.toString(buf.array()));
System.out.println("----------------------------------------------");
buf.rewind();
buf.order(ByteOrder.LITTLE_ENDIAN);
buf.asCharBuffer().put("abcde");
System.out.println(Arrays.toString(buf.array()));
打印结果:
[0, 97, 0, 98, 0, 99, 0, 100, 0, 101]
----------------------------------------------
[0, 97, 0, 98, 0, 99, 0, 100, 0, 101]
----------------------------------------------
[97, 0, 98, 0, 99, 0, 100, 0, 101, 0]
*************************************************************************************************************************
代码段二:
ByteBuffer buf = ByteBuffer.wrap(new byte[10]);
buf.asCharBuffer().put("abcde");
System.out.println(Arrays.toString(buf.array()));
System.out.println("----------------------------------------------");
buf.rewind();
buf.order(ByteOrder.BIG_ENDIAN);
buf.asCharBuffer().put("abcde");
System.out.println(Arrays.toString(buf.array()));
System.out.println("----------------------------------------------");
buf.rewind();
buf.order(ByteOrder.LITTLE_ENDIAN);
//buf.asCharBuffer().put("abcde");
System.out.println(Arrays.toString(buf.array()));
打印结果:
[0, 97, 0, 98, 0, 99, 0, 100, 0, 101]
----------------------------------------------
[0, 97, 0, 98, 0, 99, 0, 100, 0, 101]
----------------------------------------------
[0, 97, 0, 98, 0, 99, 0, 100, 0, 101]
***********************************************************************************************************************
代码段三:
ByteBuffer buf = ByteBuffer.wrap(new byte[10]);
buf.asCharBuffer().put("abcde");
System.out.println(Arrays.toString(buf.array()));
System.out.println("----------------------------------------------");
buf.rewind();
buf.order(ByteOrder.BIG_ENDIAN);
buf.asCharBuffer().put("abcde");
System.out.println(Arrays.toString(buf.array()));
System.out.println("----------------------------------------------");
buf.rewind();
buf.asCharBuffer().put("abcde");
buf.order(ByteOrder.LITTLE_ENDIAN);
System.out.println(Arrays.toString(buf.array()));
打印结果:
[0, 97, 0, 98, 0, 99, 0, 100, 0, 101]
----------------------------------------------
[0, 97, 0, 98, 0, 99, 0, 100, 0, 101]
----------------------------------------------
[0, 97, 0, 98, 0, 99, 0, 100, 0, 101]
ByteBuffer
order(ByteOrder bo)
修改此缓冲区的字节顺序。
当我们调用这一方法时,必须首先用order(ByteOrder bo)方法设置字节的排放次序,然后再向缓冲中存入相应的值,这个时候才能得到我们想要的结果(字节的存放顺序是根据CPU的不同而不同)。
代码段四:
public static void long2Byte(long num) {
ByteBuffer buf = ByteBuffer.wrap(new byte[8]);
buf.asLongBuffer().put(num);
System.out.println("默认的字节存放顺序:"+Arrays.toString(buf.array()) +"\n当前字节存放方式:"+buf.order());
buf.order(ByteOrder.LITTLE_ENDIAN);
buf.asLongBuffer().put(num);
System.out.println("改后的字节存放顺序:"+Arrays.toString(buf.array()) +"\n改后字节存放方式:"+buf.order());
}
打印结果:传入参数为:8L
默认的字节存放顺序:[0, 0, 0, 0, 0, 0, 0, 126]
当前字节存放方式:BIG_ENDIAN
改后的字节存放顺序:[126, 0, 0, 0, 0, 0, 0, 0]
改后字节存放方式:LITTLE_ENDIAN
******************************************************************************************************************************
代码段五:
public static void long2Byte(long num) {
ByteBuffer buf = ByteBuffer.wrap(new byte[8]);
buf.asLongBuffer().put(num);
System.out.println("默认的字节存放顺序:"+Arrays.toString(buf.array()) +"\n当前字节存放方式:"+buf.order());
buf.asLongBuffer().put(num);
buf.order(ByteOrder.LITTLE_ENDIAN);
System.out.println("改后的字节存放顺序:"+Arrays.toString(buf.array()) +"\n改后字节存放方式:"+buf.order());
}
打印结果:传入参数为:8L
默认的字节存放顺序:[0, 0, 0, 0, 0, 0, 0, 126]当前字节存放方式:BIG_ENDIAN
改后的字节存放顺序:[0, 0, 0, 0, 0, 0, 0, 126]
改后字节存放方式:LITTLE_ENDIAN
代码段六:
/*
* long 与byte[]之间的转换
*/
public static byte[] long2Byte(long num) {
ByteBuffer buf = ByteBuffer.wrap(new byte[8]);
buf.asLongBuffer().put(num);
System.out.println("默认的字节存放顺序:"+Arrays.toString(buf.array()) +"\n当前字节存放方式:"+buf.order());
buf.rewind();
buf.order(ByteOrder.LITTLE_ENDIAN);
buf.asLongBuffer().put(num);
System.out.println("改后的字节存放顺序:"+Arrays.toString(buf.array()) +"\n改后字节存放方式:"+buf.order());
byte[] array = Arrays.copyOfRange(buf.array(), 0, 2);//将当前8字节数组截取位2个字节长度
return array;
}
public static long byte2Long(byte[] b) {
byte[] copyOf = Arrays.copyOf(b, 8); //将2个字节长度的数组还原成8个长度的数组
ByteBuffer buf = ByteBuffer.wrap(copyOf);
System.out.println("默认的字节存放顺序:"+Arrays.toString(buf.array()) +"\n当前字节存放方式:"+buf.order());
buf.order(ByteOrder.LITTLE_ENDIAN);
System.out.println("改后的字节存放顺序:"+Arrays.toString(buf.array()) +"\n改后字节存放方式:"+buf.order());
return buf.asLongBuffer().get();
}
测试:
public static void main(String[] args) {
byte[] b = ConvertUtils.long2Byte(126);
System.out.println(Arrays.toString(b));
long l = ConvertUtils.byte2Long(b);
System.out.println(l);
}
打印结果:
默认的字节存放顺序:[0, 0, 0, 0, 0, 0, 0, 126]
当前字节存放方式:BIG_ENDIAN
改后的字节存放顺序:[126, 0, 0, 0, 0, 0, 0, 0]
改后字节存放方式:LITTLE_ENDIAN
[126, 0]
默认的字节存放顺序:[126, 0, 0, 0, 0, 0, 0, 0]
当前字节存放方式:BIG_ENDIAN
改后的字节存放顺序:[126, 0, 0, 0, 0, 0, 0, 0]
改后字节存放方式:LITTLE_ENDIAN
126
以上测试表明:
1.我们对一个小于2个字节长度的long型数字传入,再通过byte2Long()方法返回出刚才输入的数字,这种方式较常用于对于网络信息的编码解码。
如果不明白为什么传入的是2个字节长度的long型数字,那么就可以将红色部分代码注释掉,将蓝色的copyOf换成传入的字节数组b,那么上述代码就是对一个long型数字的高位和地位存放顺序的调换的工具类。因本人当前为高位存储,所以将其转成了低位存放方式。最后再将其转换为高位得到原始输入的数据。
2.我们关注一下绿色的打印内容,会发现我们明明已经对输入的字节数组进行了低位转换,可是输出时发现没有任何变化,由此得知:
ByteBuffer 的order(ByteOrder.LITTLE_ENDIAN);这个方法只对非字节数组类型的变量有效。因为我们直接操作字节数组的时候,无论CPU如何存放,我们已经手动规定死了字节的存放顺序。所以这一方法无影响。