[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();
}
}
```
- 第一章 开篇寄语
- 1-1 技术选型要点
- 1-2 认识905.4王国的交流规范
- 1-3 联系作者
- 第二章 Socket编程的基础知识
- 2-1 Socket家族的基石
- 2-2 byte数组基础
- 2-3 缓冲区基础
- 2-4 NIO Socket通讯的工作原理
- 第三章 905.4规范解读
- 3-1 基于通道选择器的Socket长连接及消息读写框架
- 3-2 严格的信件收发员
- 3-3 负责消息处理的一家子
- 3-4 负责认证的大儿子(AuthWorker)
- 3-5 哑巴老二(PingWoker)
- 3-6 勤奋的定位汇报员老三(LocationReportWorker)
- 3-7 精明的老四(BusinessReportWorker)
- 3-8 数据检察官——CRC16-CCITT校验
- 3-11 数据的加密官
- 3-12 头尾标识转义
- 第四章 测试方法
- 4-1 测试数据样例
- 4-2 客户端链路保持功能实现
- 4-3 使用Socket短连接进行功能测试
- 4-4 NIO服务端性能分析
- 4-5 http测试方法(推荐)
- 第五章 从NIO到netty
- 5-1 编程进阶——Netty核心基础
- 5-2 Netty使用常见问题
- 5-3 使用Netty重写Server端
- 5-4 Netty之链路管理
- 5-5 netty堆外内存泄漏如何应对?
- 第六章 统计与监控
- 6-1 Grafana监控面板
- 第七章 售后服务
- 7-1 勘误与优化
- 7-2 获取源码