1. 什么是大小端
大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;
小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低,和我们的逻辑方法一致。

- JVM默认为大端模式
- ByteOrder.nativeOrder()方法返回的是本地操作系统默认的处理方式,与JVM无关
2 踩坑
问题:01 02 03 (16进制),现在假设该数据类型为大端模式,如何得到该数据的小端模式?
答案:根据定义该数据的小端模式应为 03 02 01
好了,这么简单的问题,花1分钟了解概念,1秒钟判定Java肯定自带工具包转换,再花1分钟找到ByteBuffer
为自己的傲慢开始买单:
假如这样写:
ByteBuffer bb = ByteBuffer.allocate(8);
byte[] bytes = Longs.toByteArray(Long.valueOf("010203", 16));
bb.put(bytes);
bb.order(ByteOrder.LITTLE_ENDIAN);
System.out.println(Arrays.toString(bb.array()));
输出:
[0, 0, 0, 0, 0, 1, 2, 3]
卧槽?啥玩意,不是应该 [3, 2, 1, 0, 0, 0, 0, 0] 的吗???
看看按字节读取
for (int i = 0; i < 8; i++) {
System.out.print(bb.get() + " ");
}
输出:
0 0 0 0 0 1 2 3
没变化,也对,毕竟大小端转换是以字节为基本单位的,肯定没变化,压根没转呗。
试试直接读个Long?
System.out.println(Arrays.toString(Longs.toByteArray(bb.getLong())));
输出:
[3, 2, 1, 0, 0, 0, 0, 0]
转了!行8,二话不说撸源码看看!
//bb.get()是单字节读取,所以不会有任何改变
public byte get() {
return hb[ix(nextGetIndex())];
}
//bb.array()也不会变化,因为原始字节数组根本没有变化
public final byte[] array() {
if (hb == null)
throw new UnsupportedOperationException();
if (isReadOnly)
throw new ReadOnlyBufferException();
return hb;
}
//getLong就涉及到了大小端转换
public long getLong() {
return Bits.getLong(this, ix(nextGetIndex(8)), bigEndian);
}
果然这俩方法都是直接返回的,不涉及大小端,getLong方法就涉及到了大小端转换,所以成功了!
小结:
这样写的话ByteBuff只是以小端模式读取,此时改变的是读取时数据类型的字节数据,
注意,原始字节数组此时根本没有变化,因为在put之前没有设置大小端模式。
那么问题来了,我要是在put之前设置了小端呢?
ByteBuffer bb = ByteBuffer.allocate(8);
byte[] bytes = Longs.toByteArray(Long.valueOf("010203", 16));
bb.order(ByteOrder.LITTLE_ENDIAN);
bb.put(bytes);
System.out.println(Arrays.toString(bb.array()));
输出:
[0, 0, 0, 0, 0, 1, 2, 3]
没变化!因为是以字节为单位put进去的本身就是一个字节数组,ByteBuffer此时是以字节为数据类型的,单字节不涉及大小端问题,所以并没有变化!
改变方案,short类型
ByteBuffer bb = ByteBuffer.allocate(6);
short[] bytes = {1, 2, 3};
bb.order(ByteOrder.LITTLE_ENDIAN);
bb.asShortBuffer().put(bytes);
System.out.println(Arrays.toString(bb.array()));
for (int i = 1; i <= 3; i++) {
System.out.print(Arrays.toString(Shorts.toByteArray(bb.getShort())) + " ");
}
输出:
[1, 0, 2, 0, 3, 0]
[0, 1] [0, 2] [0, 3]
翻转了!看来之前的想法是对的!
等等!为啥getShort又给翻回来了???[0, 1] [0, 2] [0, 3] 这是个什么玩意?
懵逼了,撸源码:
static short getShortL(ByteBuffer bb, int bi) {
return makeShort(bb._get(bi + 1),
bb._get(bi ));
}
妈耶,真的是判断走了getShortL小端处理Short方法,专门把字节翻过来了。
开始思考:
如果
bb.order(ByteOrder.LITTLE_ENDIAN);
在put之前设置,则表示原始数据为小端
注意是为小端,意思是原始数据就是小端模式,ByteBuffer会把原始数据按数据类型翻转为大端存储
比如 0001 0002 0003 short类型,存储结果为 0100 0200 0300
此时,如果getShort,则会再次翻转字节结果为 1 2 3,这样程序才能拿到真正的值!逻辑没毛病!
总结:
使用ByteBuffer处理大小端时,应该为两种情景:
读取原始小端数据
此时应在put之前设置bb.order(ByteOrder.LITTLE_ENDIAN); 表示原始数据为小端模式,保证程序get数据类型时得到正确值
发送数据转换为小端
在put数据之前不应该设置bb.order(ByteOrder.LITTLE_ENDIAN); 意为表示原始数据为大端模式
应在get之前设置 bb.order(ByteOrder.LITTLE_ENDIAN); 此时读取的数据为小端模式,转换为字节数组后即为小端模式
大小端问题始终是以数据类型为基准转换的
例如 0001 00010002(short类型+int类型,大端)
小端模式应为 0100 02000100