问题:
想要进行二进制转换为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
数据超出范围!
问题原因分析
于是去阅读了这段源码
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都是完全可行的