碰到需要在基本类型数据和字节数组之间相互转换的问题,但我查了基础类型的包装类,似乎没有可用的方法;使用 IO 流又不够装X,所以干脆搞个工具类,里面写些相互转换的方法,后续使用起来也比较方便。
工具类源码:
import java.io.*;
/**
* 此工具类用于字节数组和基本数据类型之间的互相转换
* java中储存数据的方式是大端字节序,高位字节在前,低位字节在后,这是人类读写数值的习惯方法。
* 以下所有的操作都是针对的大端字节序
*/
public class ByteArrayUtils {
//分析int转byte[]
public static byte[] testIntToByteArray(int value) {
//int占4个字节
byte[] bytes = new byte[4];
/**
* 使用>>位移运算符,并强转byte依次取得高位字节
* 由于是大端字节序,高位到低位是从左到右
* 例如 int num = 0x060A0B02,高位到低位依次是:06 -> 0A -> 0B -> 02
* 这个int类型变量的值的16进制为:06 0A 0B 02
*/
//右移24位后,返回值00 00 00 06,强转之后变成06
bytes[0] = (byte) (value >> 24);
//右移16位后,返回值00 00 06 0A,强转之后变成0A
bytes[1] = (byte) (value >> 16);
//右移16位后,返回值00 06 0A 0B,强转之后变成0B
bytes[2] = (byte) (value >> 8);
//06 0A 0B 02,强转之后变成02
bytes[3] = (byte) value;
return bytes;
}
//分析byte[]转int
public static int testByteArrayToInt(byte[] bytes) {
/**
* byte类型的数字要&0xFF再赋值给int类型,
* 其本质原因就是想保持二进制补码(负数用补码表示)的一致性
*/
int ff = 0xFF; //00 00 00 FF
//假设字节数组为 06 0A 0B 02
//左移24位之后,h0为06 00 00 00
int h0 = (bytes[0] & ff) << 24;
//左移16位之后,h1为00 0A 00 00
int h1 = (bytes[1] & ff) << 16;
//左移8位之后,h2为00 00 0B 00
int h2 = (bytes[2] & ff) << 8;
//h3为00 00 00 02
int h3 = bytes[3] & ff;
int value = h0 | h1 | h2 | h3;
return value;
}
/**
* short转byte[]
* @param value
* @return byte[]
*/
public static byte[] shortToByteArray(short value){
byte[] bytes = new byte[2];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) (value >> (bytes.length - i - 1) * 8);
}
return bytes;
}
/**
* byte[]转short
* @param bytes
* @return short
*/
public static short byteArrayToShort(byte[] bytes){
int ff = 0xFF;
short value = 0;
for (int i = 0; i < bytes.length; i++) {
value |= (bytes[i] & ff) << (bytes.length - i - 1) * 8;
}
return value;
}
/**
* int转byte[]
* @param value
* @return byte[]
*/
public static byte[] intToByteArray(int value) {
byte[] bytes = new byte[4];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) (value >> (bytes.length - i - 1) * 8);
}
return bytes;
}
/**
* byte[]转int
* @param bytes
* @return int
*/
public static int byteArrayToInt(byte[] bytes) {
int ff = 0xFF;
int value = 0;
for (int i = 0; i < bytes.length; i++) {
value |= (bytes[i] & ff) << (bytes.length - i - 1) * 8;
}
return value;
}
/**
* long转byte[]
* @param value
* @return byte[]
*/
public static byte[] longToByteArray(long value){
byte[] bytes = new byte[8];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) (value >> (bytes.length - i - 1) * 8);
}
return bytes;
}
/**
* byte[]转long
* @param bytes
* @return long
*/
public static long byteArrayToLong(byte[] bytes){
int ff = 0xFF;
long value = 0;
for (int i = 0; i < bytes.length; i++) {
value |= (bytes[i] & ff) << (bytes.length - i - 1) * 8;
}
return value;
}
/**
* float转byte[]
* @param value
* @return byte[]
*/
public static byte[] floatToByteArray(float value){
int bitLayoutIntValue = Float.floatToIntBits(value);
byte[] bytes = new byte[4];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) (bitLayoutIntValue >> (bytes.length - i - 1) * 8);
}
return bytes;
}
/**
* byte[]转float
* @param bytes
* @return float
*/
public static float byteArrayToFloat(byte[] bytes){
int ff = 0xFF;
int bitLayoutIntValue = 0;
for (int i = 0; i < bytes.length; i++) {
bitLayoutIntValue |= (bytes[i] & ff) << (bytes.length - i - 1) * 8;
}
return Float.intBitsToFloat(bitLayoutIntValue);
}
/**
* double转byte[]
* @param value
* @return byte[]
*/
public static byte[] doubleToByteArray(double value){
long bitLayoutLongValue = Double.doubleToLongBits(value);
byte[] bytes = new byte[8];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) (bitLayoutLongValue >> (bytes.length - i - 1) * 8);
}
return bytes;
}
/**
* byte[]转double
* @param bytes
* @return double
*/
public static double byteArrayToDouble(byte[] bytes){
long ff = 0xFF;
long bitLayoutLongValue = 0;
for (int i = 0; i < bytes.length; i++) {
bitLayoutLongValue |= (bytes[i] & ff) << (bytes.length - i - 1) * 8;
}
return Double.longBitsToDouble(bitLayoutLongValue);
}
/**
* 文件转byte[],IO流
* @param file
* @return byte[]
*/
public static byte[] fileToByteArray(File file) {
byte[] dest = null;
InputStream is = null;
try {
is = new BufferedInputStream(new FileInputStream(file));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] flushBuffer = new byte[1024];
int readLen = -1;
while ((readLen = is.read(flushBuffer)) != -1) {
baos.write(flushBuffer, 0, readLen);
}
baos.flush();
dest = baos.toByteArray();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return dest;
}
/**
* byte[]转文件,IO流
* @param src
* @param dest
*/
public static void byteArrayToFile(byte[] src, File dest) {
OutputStream os = null;
try {
os = new BufferedOutputStream(new FileOutputStream(dest));
InputStream is = new ByteArrayInputStream(src);
byte[] flushBuffer = new byte[1024];
int readLen = -1;
while ((readLen = is.read(flushBuffer)) != -1) {
os.write(flushBuffer, 0, readLen);
}
os.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
上面代码中头两个方法是测试分析 int 和 byte[] 之间的相互转换的,一通百通,弄明白 int 类型,那么剩下的其他基本类型的转换也就简单了。
这里值得注意是 float 和 double,转换时需要用到其包装类的 native 方法,以 double 为例,Double.longBitsToDouble(bitLayoutLongValue) 是将 8 位 long 类型整数按照其位布局转换为相应的 double 浮点数;Double.doubleToLongBits(value) 是按照 double 类型的浮点数的位布局转换为 long 整数。float 类似。
float 占 4 个字节,int 也占 4 个字节,刚好可以把某个 float 的位布局看作一个 int 类型的整数;double 占 8 个字节,long 也占 8 个字节,刚好可以把某个 doube 的位布局看作一个 long 类型的整数。
还有一点需要注意的是 byte & 0xFF 这个骚操作了,如果转换的基本类型是个负数,那么 & 0xFF 就很有必要了。
当 byte 类型的负数自动转化为 int 类型参与运算时,高的 24 位必然会补 1,这样,其二进制补码其实已经不一致了,& 0xFF 可以将高的 24 位“清算”为 0,低 8 位保持原样。这样做的目的就是为了保证二进制数据的一致性。
试运行下面代码:
public static void main(String[] args) {
int num = -0x060A0B02;
System.out.println(num);
System.out.println();
byte[] bytes = ByteArrayUtils.intToByteArray(num);
for (int i = 0; i < bytes.length; i++) {
System.out.println(bytes[i]);
}
}
运行结果:
-101321474
-7
-11
-12
-2
人脑外加手动分析:
-101321474
-7 --> 0000 0111 --> 1111 1111 1111 1111 1111 1111 1111 1001
&
0000 0000 0000 0000 0000 0000 1111 1111 -->
0000 0000 0000 0000 0000 0000 1111 1001
<<24
--> 1111 1001 0000 0000 0000 0000 0000 0000 --> int t0
-11 --> 0000 1011 --> 1111 1111 1111 1111 1111 1111 1111 0101
&
0000 0000 0000 0000 0000 0000 1111 1111 -->
0000 0000 0000 0000 0000 0000 1111 0101
<<16
--> 0000 0000 1111 0101 0000 0000 0000 0000 --> int t1
-12 --> 0000 1100 --> 1111 1111 1111 1111 1111 1111 1111 0100
&
0000 0000 0000 0000 0000 0000 1111 1111 -->
0000 0000 0000 0000 0000 0000 1111 0100
<<8
--> 0000 0000 0000 0000 1111 0100 0000 0000 --> int t2
-2 --> 0000 0010 --> 1111 1111 1111 1111 1111 1111 1111 1110
&
0000 0000 0000 0000 0000 0000 1111 1111 -->
0000 0000 0000 0000 0000 0000 1111 1110 --> int t3
t0 | t1 | t2 | t3 --> 1111 1001 1111 0101 1111 0100 1111 1110
--> int value = -101321474 = -0x060A0B02
& 0xFF 在很多场合都有很大作为,这里需要引起我们的注意!
小小总结,到此为止。