Java基础数据类型二进制转换

前言:

      本文主要介绍java基础类型的二进制转换和二进制的基本概念。

二进制:

      1,二进制是以0和1为码,逢2进1,比如3=11=1*2+1。

      2,在计算机当中其它进制的算法基本基于2进制,因为计算机只认识0和1,比如16进制数则意味着一位16进制码由4位2进制码数表示。

      3,在Java语言中byte代表最小计量单位,byte由8位2进制数组成。

 

Java基本数据类型与表示范围(boolean忽略)

      1,byte(整型):8位,short(整型):16位,char(字符型):16位,int(整型):32位,float(浮点型单精度):32位,long(整型):64位,double(浮点型双精度):64位。

      2,直接按位做比例运算,一个short数字或char字符可有两个byte数字表示,一个float或int数字可由4个byte数字表示,一个long或double数字可由8个byte数字表示。

      3,以2进制数表示范围计算得到各整型数值表示范围,每种数值类型的最大表示范围可以通过包装类的静态属性取到,比如Integer.MAX_VALUE。

      4,char类型是最具话题性的数据类型,它承担着整个语言世界的符号统统转换称为计算机所认识的编码格式的重任,关于字符集和编码包含了很多概念也带来了很多问题。

 

二进制与编码

      一般对英文字符而言,一个字节表示一个字符,但是对汉字而言,由于低位的编码已经被使用(早期计算机并不支持中文,因此为了扩展支持,唯一的办法就是采用更多的字节数)只好向高位扩展。

      一般字符集编码的范围 utf-8>gbk>iso-8859-1(latin1)>ascll。ascll编码是美国标准信息交换码的英文缩写,包含了常用的字符,如阿拉伯数字,英文字母和一些打印符号,请注意字符和数字的区别,比如'0'字符对应的十进制数字是48。

      unicode编码包含很多种格式,utf-8是其中最常用的一种,utf-8名称的来自于该编码使用8位一个字节表示一个字符。对于一个汉字而言,它需要3个字节表示一个汉字,但大中华地区人民表示不服,搞一套gbk编码格式,用两个字节表示一个汉字。

 

乱码问题:

      乱码问题的根源在于信息解码和编码时使用的格式不统一,参考IBM社区的一篇文章《深入分析Java中文编码问题》。

 

示例1--简单&运算示例

    @Test
    public void demo(){
        // 0100&0100=0100
        assertEquals(4,4&4);
        // 1000&0100=0000
        assertEquals(0,4&8);
        
        // 字符'0'用十进制数48表示
        char c = '0';
        int i = c;
        assertEquals(48,i);
        
        // 认识 0xf, 0xff, 0xffff. f对应16进制数字15, 二进制1111
        byte b = Byte.MAX_VALUE;
        assertEquals(127,b);
        assertEquals(127,b & 0xff);
        assertEquals(15,b & 0xf);
        
        byte b1 =  -1;
        // 说明-1是按照默认的(byte)b1=0xff与0xff计算后转换为int值 = 255
        assertEquals(255, (byte)b1&0xff);
        assertEquals(255, b1&0xff);
        // 255转换为byte 结果为-1
        assertEquals(-1, (byte)(b1&0xff));
        // b1不强制转换编译器会报错. 0xff超过byte表示范围转为byte值得到-1
        byte bf = (byte) 0xff;
        assertEquals(bf, -1);
        // 0xf2 = 11110010
        // 符号位不变取反+1 = 10001101+00000001 = 10001110 = -14
        assertEquals(-14, (byte)0xf2);
    }

     上面的代码包含了几个基本的运算和转换的示例。

 

二进制与其它类型直接的转换

     工具类ByteUtils.java(为方便了解进制转换,各个方法参数都不包含index,如果要作为工具使用,请自行修改)

package org.wit.ff.common;

/**
 * 
 * <pre>
 * 基本数据类型转换(主要是byte和其它类型之间的互转).
 * </pre>
 *
 * @author F.Fang
 * @version $Id: ByteUtils.java, v 0.1 2014年11月9日 下午11:23:21 F.Fang Exp $
 */
public class ByteUtils {
    
    /**
     * 
     * <pre>
     * 将4个byte数字组成的数组合并为一个float数.
     * </pre>
     * 
     * @param arr
     * @return
     */
    public static float byte4ToFloat(byte[] arr) {
        if (arr == null || arr.length != 4) {
            throw new IllegalArgumentException("byte数组必须不为空,并且是4位!");
        }
        int i = byte4ToInt(arr);
        return Float.intBitsToFloat(i);
    }

    /**
     * 
     * <pre>
     * 将一个float数字转换为4个byte数字组成的数组.
     * </pre>
     * 
     * @param f
     * @return
     */
    public static byte[] floatToByte4(float f) {
        int i = Float.floatToIntBits(f);
        return intToByte4(i);
    }

    /**
     * 
     * <pre>
     * 将八个byte数字组成的数组转换为一个double数字.
     * </pre>
     * 
     * @param arr
     * @return
     */
    public static double byte8ToDouble(byte[] arr) {
        if (arr == null || arr.length != 8) {
            throw new IllegalArgumentException("byte数组必须不为空,并且是8位!");
        }
        long l = byte8ToLong(arr);
        return Double.longBitsToDouble(l);
    }

    /**
     * 
     * <pre>
     * 将一个double数字转换为8个byte数字组成的数组.
     * </pre>
     * 
     * @param i
     * @return
     */
    public static byte[] doubleToByte8(double i) {
        long j = Double.doubleToLongBits(i);
        return longToByte8(j);
    }

    /**
     * 
     * <pre>
     * 将一个char字符转换为两个byte数字转换为的数组.
     * </pre>
     * 
     * @param c
     * @return
     */
    public static byte[] charToByte2(char c) {
        byte[] arr = new byte[2];
        arr[0] = (byte) (c >> 8);
        arr[1] = (byte) (c & 0xff);
        return arr;
    }

    /**
     * 
     * <pre>
     * 将2个byte数字组成的数组转换为一个char字符.
     * </pre>
     * 
     * @param arr
     * @return
     */
    public static char byte2ToChar(byte[] arr) {
        if (arr == null || arr.length != 2) {
            throw new IllegalArgumentException("byte数组必须不为空,并且是2位!");
        }
        return (char) (((char) (arr[0] << 8)) | ((char) arr[1]));
    }

    /**
     * 
     * <pre>
     * 将一个16位的short转换为长度为2的8位byte数组.
     * </pre>
     * 
     * @param s
     * @return
     */
    public static byte[] shortToByte2(Short s) {
        byte[] arr = new byte[2];
        arr[0] = (byte) (s >> 8);
        arr[1] = (byte) (s & 0xff);
        return arr;
    }

    /**
     * 
     * <pre>
     * 长度为2的8位byte数组转换为一个16位short数字.
     * </pre>
     * 
     * @param arr
     * @return
     */
    public static short byte2ToShort(byte[] arr) {
        if (arr != null && arr.length != 2) {
            throw new IllegalArgumentException("byte数组必须不为空,并且是2位!");
        }
        return (short) (((short) arr[0] << 8) | ((short) arr[1] & 0xff));
    }

    /**
     * 
     * <pre>
     * 将short转换为长度为16的byte数组.
     * 实际上每个8位byte只存储了一个0或1的数字
     * 比较浪费.
     * </pre>
     * 
     * @param s
     * @return
     */
    public static byte[] shortToByte16(short s) {
        byte[] arr = new byte[16];
        for (int i = 15; i >= 0; i--) {
            arr[i] = (byte) (s & 1);
            s >>= 1;
        }
        return arr;
    }

    public static short byte16ToShort(byte[] arr) {
        if (arr == null || arr.length != 16) {
            throw new IllegalArgumentException("byte数组必须不为空,并且长度为16!");
        }
        short sum = 0;
        for (int i = 0; i < 16; ++i) {
            sum |= (arr[i] << (15 - i));
        }
        return sum;
    }

    /**
     * 
     * <pre>
     * 将32位int转换为由四个8位byte数字.
     * </pre>
     * 
     * @param sum
     * @return
     */
    public static byte[] intToByte4(int sum) {
        byte[] arr = new byte[4];
        arr[0] = (byte) (sum >> 24);
        arr[1] = (byte) (sum >> 16);
        arr[2] = (byte) (sum >> 8);
        arr[3] = (byte) (sum & 0xff);
        return arr;
    }

    /**
     * <pre>
     * 将长度为4的8位byte数组转换为32位int.
     * </pre>
     * 
     * @param arr
     * @return
     */
    public static int byte4ToInt(byte[] arr) {
        if (arr == null || arr.length != 4) {
            throw new IllegalArgumentException("byte数组必须不为空,并且是4位!");
        }
        return (int) (((arr[0] & 0xff) << 24) | ((arr[1] & 0xff) << 16) | ((arr[2] & 0xff) << 8) | ((arr[3] & 0xff)));
    }

    /**
     * 
     * <pre>
     * 将长度为8的8位byte数组转换为64位long.
     * </pre>
     * 
     * 0xff对应16进制,f代表1111,0xff刚好是8位 byte[]
     * arr,byte[i]&0xff刚好满足一位byte计算,不会导致数据丢失. 如果是int计算. int[] arr,arr[i]&0xffff
     * 
     * @param arr
     * @return
     */
    public static long byte8ToLong(byte[] arr) {
        if (arr == null || arr.length != 8) {
            throw new IllegalArgumentException("byte数组必须不为空,并且是8位!");
        }
        return (long) (((long) (arr[0] & 0xff) << 56) | ((long) (arr[1] & 0xff) << 48) | ((long) (arr[2] & 0xff) << 40)
                        | ((long) (arr[3] & 0xff) << 32) | ((long) (arr[4] & 0xff) << 24)
                        | ((long) (arr[5] & 0xff) << 16) | ((long) (arr[6] & 0xff) << 8) | ((long) (arr[7] & 0xff)));
    }

    /**
     * 将一个long数字转换为8个byte数组组成的数组.
     */
    public static byte[] longToByte8(long sum) {
        byte[] arr = new byte[8];
        arr[0] = (byte) (sum >> 56);
        arr[1] = (byte) (sum >> 48);
        arr[2] = (byte) (sum >> 40);
        arr[3] = (byte) (sum >> 32);
        arr[4] = (byte) (sum >> 24);
        arr[5] = (byte) (sum >> 16);
        arr[6] = (byte) (sum >> 8);
        arr[7] = (byte) (sum & 0xff);
        return arr;
    }

    /**
     * 
     * <pre>
     * 将int转换为32位byte.
     * 实际上每个8位byte只存储了一个0或1的数字
     * 比较浪费.
     * </pre>
     * 
     * @param num
     * @return
     */
    public static byte[] intToByte32(int num) {
        byte[] arr = new byte[32];
        for (int i = 31; i >= 0; i--) {
            // &1 也可以改为num&0x01,表示取最地位数字.
            arr[i] = (byte) (num & 1);
            // 右移一位.
            num >>= 1;
        }
        return arr;
    }

    /**
     * 
     * <pre>
     * 将长度为32的byte数组转换为一个int类型值.
     * 每一个8位byte都只存储了0或1的数字.
     * </pre>
     * 
     * @param arr
     * @return
     */
    public static int byte32ToInt(byte[] arr) {
        if (arr == null || arr.length != 32) {
            throw new IllegalArgumentException("byte数组必须不为空,并且长度是32!");
        }
        int sum = 0;
        for (int i = 0; i < 32; ++i) {
            sum |= (arr[i] << (31 - i));
        }
        return sum;
    }

    /**
     * 
     * <pre>
     * 将长度为64的byte数组转换为一个long类型值.
     * 每一个8位byte都只存储了0或1的数字.
     * </pre>
     * 
     * @param arr
     * @return
     */
    public static long byte64ToLong(byte[] arr) {
        if (arr == null || arr.length != 64) {
            throw new IllegalArgumentException("byte数组必须不为空,并且长度是64!");
        }
        long sum = 0L;
        for (int i = 0; i < 64; ++i) {
            sum |= ((long) arr[i] << (63 - i));
        }
        return sum;
    }

    /**
     * 
     * <pre>
     * 将一个long值转换为长度为64的8位byte数组.
     * 每一个8位byte都只存储了0或1的数字.
     * </pre>
     * 
     * @param sum
     * @return
     */
    public static byte[] longToByte64(long sum) {
        byte[] arr = new byte[64];
        for (int i = 63; i >= 0; i--) {
            arr[i] = (byte) (sum & 1);
            sum >>= 1;
        }
        return arr;
    }


}

 

测试用例:

    1,测试基本的移位操作符号.

    @Test
    public void op(){
        // >>>
        // >>
        // <<<
        int i = -1;
        System.out.println(Arrays.toString(ByteUtils.intToByte32(i)));
        
        // 有符号右移,符号位依然存在,右移一位后最左边的最高位补1, -1
        System.out.println(i >> 1);
        
        // 无符号右移,忽略符号位,右移一位后最左边的最高位补0,2147483647 Integer.MAX_VALUE
        System.out.println(i >>> 1);
        
        // 有符号左移,符号位依然存在,左移一位最低位补0. -2
        System.out.println(i << 1);
        // <<<无此符号,因为左移符号位不会丢失,没有必要再增加此操作.
    }

     2,查询最大值最小值.

    @Test
    public void showMaxValAndminVal() {
        // 127
        System.out.println(Byte.MAX_VALUE);
        // -128
        System.out.println(Byte.MIN_VALUE);

        // 32767
        System.out.println(Short.MAX_VALUE);
        // -32768
        System.out.println(Short.MIN_VALUE);

        // 65535 2的16次方-1
        System.out.println((int) Character.MAX_VALUE);
        // 0
        System.out.println((int) Character.MIN_VALUE);

        // 2147483647
        System.out.println(Integer.MAX_VALUE);
        // -2147483648
        System.out.println(Integer.MIN_VALUE);

        // 科学计数法.
        // 3.4028235E38
        System.out.println(Float.MAX_VALUE);
        // 1.4E-45
        System.out.println(Float.MIN_VALUE);

        // 9223372036854775807
        System.out.println(Long.MAX_VALUE);
        // -9223372036854775808
        System.out.println(Long.MIN_VALUE);

        // 科学计数法.
        // 1.7976931348623157E308
        System.out.println(Double.MAX_VALUE);
        // 4.9E-324
        System.out.println(Double.MIN_VALUE);
    }

      3,转换测试:

    @Test
    public void transByte() {
        char c = 'z';
        byte[] charToByte2Arr = ByteUtils.charToByte2(c);
        assertEquals("[0, 122]", Arrays.toString(charToByte2Arr));
        assertEquals('z', ByteUtils.byte2ToChar(charToByte2Arr));

        short s = Short.MAX_VALUE;
        // System.out.println("Short.MAX_VALUE:" + s);
        byte[] shortToByte2Arr = ByteUtils.shortToByte2(s);
        assertEquals("[127, -1]", Arrays.toString(shortToByte2Arr));
        assertEquals(s, ByteUtils.byte2ToShort(shortToByte2Arr));

        byte[] shortToByte16 = ByteUtils.shortToByte16(s);
        System.out.println(Arrays.toString(shortToByte16));
        System.out.println(ByteUtils.byte16ToShort(shortToByte16));

        int i = Integer.MAX_VALUE;
        // System.out.println("Integer.MAX_VALUE:" + i);
        byte[] intToByte4Arr = ByteUtils.intToByte4(i);
        assertEquals("[127, -1, -1, -1]", Arrays.toString(intToByte4Arr));
        assertEquals(i, ByteUtils.byte4ToInt(intToByte4Arr));

        byte[] intToByte32Arr = ByteUtils.intToByte32(i);
        System.out.println(Arrays.toString(intToByte32Arr));
        System.out.println(ByteUtils.byte32ToInt(intToByte32Arr));

        long j = Long.MAX_VALUE;
        // System.out.println("Long.MAX_VALUE:" + j);
        byte[] longToByte8Arr = ByteUtils.longToByte8(j);
        assertEquals("[127, -1, -1, -1, -1, -1, -1, -1]", Arrays.toString(longToByte8Arr));
        assertEquals(j, ByteUtils.byte8ToLong(longToByte8Arr));

        byte[] longToByte64Arr = ByteUtils.longToByte64(j);
        System.out.println(Arrays.toString(longToByte64Arr));
        System.out.println(ByteUtils.byte64ToLong(longToByte64Arr));

        double d = 2.34;
        byte[] doubleToByte8Arr = ByteUtils.doubleToByte8(d);
        assertEquals("[64, 2, -72, 81, -21, -123, 30, -72]", Arrays.toString(doubleToByte8Arr));
        // double类型不能直接做等值比较,只能在精度范围做差值比较.
        assertEquals(d + "", ByteUtils.byte8ToDouble(doubleToByte8Arr) + "");

        float f = 1.2f;
        byte[] floatToByte4Arr = ByteUtils.floatToByte4(f);
        assertEquals("[63, -103, -103, -102]", Arrays.toString(floatToByte4Arr));
        assertEquals(f + "", ByteUtils.byte4ToFloat(floatToByte4Arr) + "");
    }