问题:

想要进行二进制转换为byte

发现java的byte包装类Byte,提供了静态方法:parseByte(String s, int radix)

作用是将字符串s转化为byte类型,radix表示这个字符串数据是什么进制

正好符合要求

一个八位的二进制数,必然在-128(11111111)到127(01111111)之间,就是一个标准的byte类型数据

出现问题是因为我这里实际数据 10101000 转化中报如下错误

Exception in thread “main” java.lang.NumberFormatException: Value out of range. Value:“10101000” Radix:2

java二进制数字转字符串怎么转 java 二进制字符串转byte_java二进制数字转字符串怎么转

数据超出范围

问题原因分析

于是去阅读了这段源码

public static byte parseByte(String s, int radix)
        throws NumberFormatException {
        int i = Integer.parseInt(s, radix);
        if (i < MIN_VALUE || i > MAX_VALUE)
            throw new NumberFormatException(
                "Value out of range. Value:\"" + s + "\" Radix:" + radix);
        return (byte)i;
    }

发现先调用了int包装类Integer的parseInt(s, radix)方法,得到int,再强制类型转换为byte

类型转换前,判断了这个int值是否在-128到127之前,如果越界就会报这个错误

debug发现Integer的parseInt返回结果为168

显然,一定是parseInt没有将字符串当做带符号位的二进制数进行处理

阅读源码:

public static int parseInt(String s, int radix)
                throws NumberFormatException
    {
        /*
         * WARNING: This method may be invoked early during VM initialization
         * before IntegerCache is initialized. Care must be taken to not use
         * the valueOf method.
         */

        if (s == null) {
            throw new NumberFormatException("null");
        }

        if (radix < Character.MIN_RADIX) {
            throw new NumberFormatException("radix " + radix +
                                            " less than Character.MIN_RADIX");
        }

        if (radix > Character.MAX_RADIX) {
            throw new NumberFormatException("radix " + radix +
                                            " greater than Character.MAX_RADIX");
        }

        int result = 0;
        boolean negative = false;
        int i = 0, len = s.length();
        int limit = -Integer.MAX_VALUE;
        int multmin;
        int digit;

        if (len > 0) {
            char firstChar = s.charAt(0);
            if (firstChar < '0') { // Possible leading "+" or "-"
                if (firstChar == '-') {
                    negative = true;
                    limit = Integer.MIN_VALUE;
                } else if (firstChar != '+')
                    throw NumberFormatException.forInputString(s);

                if (len == 1) // Cannot have lone "+" or "-"
                    throw NumberFormatException.forInputString(s);
                i++;
            }
            multmin = limit / radix;
            while (i < len) {
                // Accumulating negatively avoids surprises near MAX_VALUE
                digit = Character.digit(s.charAt(i++),radix);
                if (digit < 0) {
                    throw NumberFormatException.forInputString(s);
                }
                if (result < multmin) {
                    throw NumberFormatException.forInputString(s);
                }
                result *= radix;
                if (result < limit + digit) {
                    throw NumberFormatException.forInputString(s);
                }
                result -= digit;
            }
        } else {
            throw NumberFormatException.forInputString(s);
        }
        return negative ? result : -result;
    }

果然如此,简单一看,发现有这样的一段判断代码,我加上注释给大家看一下(只是解决这个问题的话,其他部分就不用看了)

//判断了首位字符如果不是数字
if (firstChar < '0') { // Possible leading "+" or "-"
    //判断了如果是减号(干了什么先不管)
    if (firstChar == '-') {
        negative = true;
        limit = Integer.MIN_VALUE;
    } 
    //如果不是减号,再判断了如果不是加号会抛出异常
    else if (firstChar != '+')
        throw NumberFormatException.forInputString(s);
	//走到这里说明是减号或加号中的一个,判断了字符串总长度,如果只有1,抛出异常,不能单独只有一个符号
    if (len == 1) // Cannot have lone "+" or "-"
        throw NumberFormatException.forInputString(s);
    i++;
}

说明parseInt方法,字符串首位可以带上±符号来表示正负

数字部分不会考虑符号位,是当做无符号数在转换

所以本应是符号数10101000(-40),实际处理成无符号数+10101000(168)

解决方案

1.第一种解决方案是,加上±号

自行将首位当做符号位判断,处理字符串首位0替换加号,1替换减号

这种方案下,表示二进制数的字符串,如果合规且长度不大于8,就百分百正确

但如果长度大于8位,还会抛出越界异常,这是正常的,因为确实越界

2.第二种解决方案是,先调用Integer的parseInt(s, radix)方法,转换为int,再将int强制类型转换为byte

这种方案,如果直接使用,其实会产生错误的结果 -88

因为强制类型转化的过程,是在操作二进制补码,最会相当于,会将传入的二进制字符串当做是补码操作,具体产生这个现象的原因,下面分析一下Java中int强制类型转换为byte的过程

这个过程,是按照2进制位在操作

    首先我们知道计算机中保存数据就都是使用补码

    也知道int占用4个字节,共32位,其中一个符号位,所以可以表示-2^31到2^31-1的数据

    那么还用 10101000 举例,10101000经过parseInt后变成了168

    分析(byte)168的操作

	1.首先计算机直接就拿到32位的补码

		0 00000000 00000000 00000001 0101000

	2.再直接丢弃掉前24位

  	剩下 1 0101000

	3.将余下八位当做是byte类型的8位补码存入byte

	完成了

  校验一下

  补码为:1 0101000

  源码是:1 1011000

  转化十进制:

          1 1011000

        = - (2^6+2^4+2^3)

        = - (64+16+8)

        = - 88

所以这种方式,其实相当于原本传入的二进制字符串就是补码,才是正确的

那么就可以先自行将原码转换为补码,然后再执行这第二种方法的操作,就是正确结果了

同样也必须保证就八位,不越界,虽然这个方式越界了也不会报错,但如果越界,相当于是先转换成的int % 256之后,再进行强转,并没有什么意义

总结:两种方法应对八位二进制符号数存入byte都是完全可行的