最近工作中遇到了大量需要处理byte,并进行类型转换的场景。由于之前从未特意关注过java的二进制处理,所以踩了很多坑,在这里总结一下。

1、由于java是没有unsigned类型的,因此在处理很多其他语言传过来的byte时,如果直接强制类型转换为short或者int有可能会因为符号位的原因得到预期之外的值。这其中的原因就是java在byte强转short/int时采用的是补符号位扩展的方式。

举个栗子:十进制129,转为二进制是1000 0001,但是由于java没有unsigned类型,第八位的1就会被识别为符号位,byte强转为short,按照符号位1进行高位补位得到1111 1111 1000 0001,十进制-128,然而我们期望得到的short值仍然是129。因此,我们要将按符号位补位改为按0补位,实现方式就是使用0xff进行“与”运算,因为0xff是一个正数int,这样进行一次与运算后,高位的1都被抹掉了,只留下了原本的8位作为数据。

因此,java语言下,byte转int的正确打开方式是
byte b = (byte)129;
int i = b & 0xff;
//也可以使用Byte类中封装的toUnsignedInt方法,实现是一样的
int i2 = Byte.toUnsignedInt(b);
System.out.println(i);
System.out.println(i2);

2、大端模式。对于接收到的数据,一定要问一下采用的是大端模式还是小端模式。以下是大小端的定义:
大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;这和我们的阅读习惯一致。
小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低。
在刚开始做字节数组转换时,想当然得以为bytes[0]就是转换后数字的低8位,byte[1]为8-16位,即采用了小端模式进行了解析,然而实际上,大端模式才是常用的方式,因为这种方式和阅读习惯一直,便于理解。

以下为总结的byte与数值、字符串以及十六进制字符串的相互转换方法,数值转换使用大端模式。

public static String bytes2HexString(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
int i = Byte.toUnsignedInt(b);
String hexString = Integer.toHexString(i);
if (hexString.length() < 2) {
sb.append(0);
}
sb.append(hexString);
}
return sb.toString();
}


public static long bytes2Long(byte[] bytes) {
long longResult = 0;
int leftShift;
for (int i=0; i<bytes.length; i++) {
byte b = bytes[i];
leftShift = 8 * (bytes.length-1 - i);
long bl = Byte.toUnsignedLong(b);
long l = bl << leftShift;
longResult = longResult | l;
}
return longResult;
}


public static byte[] long2Bytes(long data, int length) {
byte[] bytes = new byte[length];
for (int i=0; i<length; i++) {

     bytes[i] = (byte) ((data >> 8*(bytes.length-1 - i)) & 0xff);
}
return bytes;
}


public static byte[] hexString2Bytes(String hexString) {
if (StringUtils.isEmpty(hexString)) {
return null;
}
hexString = hexString.toLowerCase();
final byte[] byteArray = new byte[hexString.length() >> 1];
int index = 0;
for (int i = 0; i < hexString.length(); i++) {

      if (index > hexString.length() - 1) {
return byteArray;
}
byte highDit = (byte) (Character.digit(hexString.charAt(index), 16) & 0xFF);
byte lowDit = (byte) (Character.digit(hexString.charAt(index + 1), 16) & 0xFF);
byteArray[i] = (byte) (highDit << 4 | lowDit);
index += 2;
}
return byteArray;
}