[toc] ## Byte数组操作 Bit 意为"位"或"比特",是计算机运算的基础单位;Byte 意为"字节",是计算机文件大小的基本计算单位。1 bit 就是1位二进制数,比如 1 或者 0;1 Byte 就是 1 个字节,1 个字节是由8个二进制位组成的。比如1111111,00000000等。 Byte是java中的八大基本数据类型之一,byte是Byte的拆箱数据类型。 ### 数据转换 我们在开发过程中,编辑器使用的数值默认是10进制的,毕竟十进制是普及到街巷市井的一个数制,即便是我们懂得二进制的人,十进制的可读性也比较习惯。而对计算机则不然,因为它的底层存储机制为二进制,但是二进制的读取对我们人类而言则不是那么方便,8位二进制正好可以使用2个十六进制数字表示,相比二进制,表述也更加简便。 举个例子,比如二进制表示表示一个字节的值为10110110,使用十六进制则可以简单表示为B6。 ### 数组截取 本案例中,我们经常会遇到字节数组的截取。 `System.arraycopy(src, srcPos, dest, destPos, length)`的使用: ```java /* @param src the source array. * @param srcPos starting position in the source array. * @param dest the destination array. * @param destPos starting position in the destination data. * @param length the number of array elements to be copied. * @exception IndexOutOfBoundsException if copying would cause * access of data outside array bounds. * @exception ArrayStoreException if an element in the <code>src</code> * array could not be stored into the <code>dest</code> array * because of a type mismatch. * @exception NullPointerException if either <code>src</code> or * <code>dest</code> is <code>null</code>. */ public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length); ``` 参数解释: - Object src : 原数组 - int srcPos : 从原数组中截取数据的起始位置 - Object dest : 目标数组 - int destPos : 目标数组的开始起始位置 - int length : 从原数组中截取数据时要copy的数组的长度 比如 :我们有一个数组数据 ```java byte[] srcBytes = new byte[]{2,4,0,0,0,0,0,10,15,50}; // 源数组 byte[] destBytes = new byte[5]; // 目标数组 System.arrayCopy(srcBytes,0,destBytes ,0,5); ``` 则最终destBytes中的元素为:`{2,4,0,0,0}`。 ### 数组合并 关于数组合并,我们还会经常用到,`commons-lang3.jar`包中的`ArrayUtils`类的`addAll()`,其功能是添加一个字节数组或者添加一个字节到原先的字节数组中。 ```java public static byte[] addAll(byte[] array1, byte... array2) { if (array1 == null) { return clone(array2); } else if (array2 == null) { return clone(array1); } else { byte[] joinedArray = new byte[array1.length + array2.length]; System.arraycopy(array1, 0, joinedArray, 0, array1.length); System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); return joinedArray; } } ``` ### ByteUtil工具类 这里提供一个用于16进制字符串与数值转换的工具类,尤其是在我们单元测试中会经常用来调试。 ```java import org.apache.commons.lang3.ArrayUtils; import java.io.*; public class ByteUtil { /** * 16进制字符集 */ private static final char HEX_DIGITS[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; private static final String HEXS = "0123456789abcdef"; /** * 将一个字节数组转换成一个int */ public static int toInt(byte[] bytes) { int i = 24; int result = 0; for (byte tmp : bytes) { result += (tmp & 0xff) << i; i -= 8; } return result; } /** * 高位字节在前 * @param n * @return */ public static byte[] intToHighByteArr(int n) { byte[] b = new byte[4]; b[3] = (byte) (n & 0xff); b[2] = (byte) (n >> 8 & 0xff); b[1] = (byte) (n >> 16 & 0xff); b[0] = (byte) (n >> 24 & 0xff); return b; } /** * 高位字节在前 * @param n * @return */ public static byte[] shortToHighByteArr(short n) { byte[] b = new byte[2]; b[1] = (byte) (n & 0xff); b[0] = (byte) (n >> 8 & 0xff); return b; } /** * 从一个byte[]数组中截取一部分 * @param src * @param begin * @param count * @return */ public static byte[] subBytes(byte[] src, int begin, int count) { byte[] bs = new byte[count]; for (int i=begin;i<begin+count; i++) { bs[i-begin] = src[i]; } return bs; } /** * 更新src数组 * @param des * @param desPos * @param count * @return */ public static byte[] modifyBytes(byte[] src,byte[] des, int desPos, int count) { System.arraycopy(src,0,des,desPos,count); return des; } /** * 将一个字节数组转换成一个short */ public static short toShort(byte[] bytes) { int i = 8; short result = 0; for (byte tmp : bytes) { result += (tmp & 0xff) << i; i -= 8; } return result; } public static String toVersion(byte[] bytes){ StringBuffer stringBuffer = new StringBuffer("V"); for (int i =0 ; i< bytes.length; i++){ if (i>0){ stringBuffer.append("."); } stringBuffer.append(bytes[i]); } return stringBuffer.toString(); } /** * @功能: BCD码转为10进制串(阿拉伯数据) * @参数: BCD码 * @结果: 10进制串 */ public static String bcd2Str(byte[] bytes) { StringBuffer temp = new StringBuffer(bytes.length * 2); for (int i = 0; i < bytes.length; i++) { temp.append((byte) ((bytes[i] & 0xf0) >>> 4)); temp.append((byte) (bytes[i] & 0x0f)); } return temp.toString().substring(0, 1).equalsIgnoreCase("0") ? temp .toString().substring(1) : temp.toString(); } /** * @功能: BCD码转为10进制串(阿拉伯数据) * @参数: BCD码 * @结果: 10进制串 */ public static String bcd62Str(byte[] bytes) { byte[] prefix = {0x20}; byte[] all = ArrayUtils.addAll(prefix, bytes); return ByteUtil.bcd2Str(all); } /** * * 将字节数组中指定区间的子数组转换成16进制字符串 * @param bytes 目标字节数组 * @param start * 起始位置(包括该位置) * @param end 结束位置(不包括该位置) * @return 转换结果 */ public static String bytesToHex(byte bytes[], int start, int end) { StringBuilder sb = new StringBuilder(); for (int i = start; i < start + end; i++) { sb.append(byteToHex(bytes[i])); } return sb.toString(); } /** * hexStr:010400xxxx * @param bytes * @return */ public static String bytesToHex(byte bytes[]) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < bytes.length; i++) { sb.append(byteToHex(bytes[i])); } return sb.toString(); } /** * 用作hex字符串的生成 * byte[]转hex字符串 * @param array * @return */ public static String bytes2HexString(byte[] array) { StringBuilder builder = new StringBuilder(); for (byte b : array) { String hex = Integer.toHexString(b & 0xFF); if (hex.length() == 1) { hex = '0' + hex; } if (hex.startsWith("8") || hex.startsWith("9") || hex.startsWith("a") || hex.startsWith("b") || hex.startsWith("c") || hex.startsWith("d") || hex.startsWith("e") || hex.startsWith("f")){ builder.append("(byte)"); } builder.append("0x").append(hex).append(","); } return builder.toString(); } //byte[]转hex字符串 public static String idcardHex(byte[] array) { StringBuilder builder = new StringBuilder(); for (byte b : array) { String hex = Integer.toHexString(b & 0xFF); if (hex.length() > 1) { throw new NumberFormatException("格式不正确"); } builder.append(hex); } return builder.toString(); } /** * 将单个字节码转换成16进制字符串 * @param bt 目标字节 * @return 转换结果 */ public static String byteToHex(byte bt) { return HEX_DIGITS[(bt & 0xf0) >> 4] + "" + HEX_DIGITS[bt & 0xf]; } /** * 将16进制转换成字节数组 * * @param hex * @return */ public static byte[] hexToBytes(String hex) { int len = (hex.length() / 2); byte[] result = new byte[len]; char[] achar = hex.toCharArray(); for (int i = 0; i < len; i++) { int pos = i * 2; result[i] = (byte) (hexToByte(achar[pos]) << 4 | hexToByte(achar[pos + 1])); } return result; } /** * 将一个16进制转换成字节数组 * * @param c * @return */ public static byte hexToByte(char c) { return (byte) HEXS.indexOf(c); } /** * 从对象获取一个字节数组 * */ public static byte[] obj2Byte(Serializable obj) { if (obj == null) { return null; } ByteArrayOutputStream bo = new ByteArrayOutputStream(); ObjectOutputStream oo = null; byte[] result = null; try { oo = new ObjectOutputStream(bo); oo.writeObject(obj); result = bo.toByteArray(); } catch (Exception e) { e.printStackTrace(); } finally { if (oo != null) { try { oo.close(); } catch (IOException e) { e.printStackTrace(); } } } return result; } /** * 从字节数组获取对象 * */ public static Object byte2Obj(byte[] objBytes) { if (objBytes == null || objBytes.length == 0) { return null; } ByteArrayInputStream bi = new ByteArrayInputStream(objBytes); ObjectInputStream oi = null; Object obj = null; try { oi = new ObjectInputStream(bi); obj = oi.readObject(); } catch (Exception e) { e.printStackTrace(); } finally { if (oi != null) { try { oi.close(); } catch (IOException e) { e.printStackTrace(); } } } return obj; } } ``` 比如下面的例子:我们可以直接使用该工具类把16进制编码的消息串,转换为字节数组。 ``` /** * 返回应答消息 * @param requestBytes * @return */ @Test public void messageDeal(){ String businessMsg = "5b0000006c01fa3a8a210001406f4501020f000000000021030000004cd5e3474436353736320000000200010c675e01044b620220210615011000010cae28044b8bf7202106150117003030393838313700000000000000000000000000000005001800000001001000d07c5d"; byte[] bytes = ByteUtil.hexToBytes(businessMsg); try { buildResponseWithAdmin(bytes,"127.0.0.1"); } catch (CredentialNotFoundException e) { e.printStackTrace(); } } ```