Java 中字节数组和基本类型之间的相互转换

社会性死亡

碰到需要在基本类型数据和字节数组之间相互转换的问题,但我查了基础类型的包装类,似乎没有可用的方法;使用 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 在很多场合都有很大作为,这里需要引起我们的注意!

小小总结,到此为止。

你可能感兴趣的:(Java 中字节数组和基本类型之间的相互转换)