Integer的parseInt与value of的原理
前言
笔者一直使用Integer的转换,包括Long,枚举等,从来没有注意它是怎么实现的,最近有个业务组转换报错了,想看看是如何实现的。据笔者猜测:ASCII码转换?这是常用的计量,什么大写变小写都是这样实现的。下面看看如何实现的吧
1. demo构建
public class StringParseInt {
public static void main(String[] args) {
String str = "-1234";
int i = Integer.parseInt(str);
int y = Integer.valueOf(str);
System.out.println(i + "\t" + y);
}
}
输出都正常,关键是看怎么实现的
2. Integer的实现方式
2.1 value of
public static Integer valueOf(String s) throws NumberFormatException {
return Integer.valueOf(parseInt(s, 10));
}
本质还是parseInt,从这点看与parseInt没有区别;但是这里有装箱,如果接收值是int建议直接使用parseInt,省去装箱的过程。
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
如果在缓存数组,直接使用,看看缓存数组怎么来的
private static class IntegerCache {
//下限固定-128
static final int low = -128;
//上限没有初始化
static final int high;
//核心缓存数组
static final Integer cache[];
//类加载初始化
static {
// high value may be configured by property
//初始127上限
int h = 127;
//VM参数java.lang.Integer.IntegerCache.high可以配置Integer的最大上限
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
//我没设置的值,如果不是int字符串型就会报错,然后被捕获
int i = parseInt(integerCacheHighPropValue);
//取大,对比127;也就是至少是127
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
// 上面官方注释很明显了,这里减128再减1是因为low是-128
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
//创建缓存数组,如果设置java.lang.Integer.IntegerCache.high,不宜设置过大,过大很占连续空间
cache = new Integer[(high - low) + 1];
int j = low;
//初始化缓存
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
//断言至少127,JLS7
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
从源码看Integer内部类装载时,会初始化一个缓存空间,存储Integer对象。很多面试时就会被这个坑了,初始化的缓存对象地址取值是一致的,可以使用==作对比;然后超过这个范围的Integer就不能了,要使用Integer的eq方法
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
因为自动装箱,实际上使用的value of方法,默认情况下,-128~127使用缓存对象。
2.2 parseInt
/**
* 这段注释尤为重要,定义了符号位,定义了10进制数字
* Parses the string argument as a signed decimal integer. The
* characters in the string must all be decimal digits, except
* that the first character may be an ASCII minus sign {@code '-'}
* ({@code '\u005Cu002D'}) to indicate a negative value or an
* ASCII plus sign {@code '+'} ({@code '\u005Cu002B'}) to
* indicate a positive value. The resulting integer value is
* returned, exactly as if the argument and the radix 10 were
* given as arguments to the {@link #parseInt(java.lang.String,
* int)} method.
*
* @param s a {@code String} containing the {@code int}
* representation to be parsed
* @return the integer value represented by the argument in decimal.
* @exception NumberFormatException if the string does not contain a
* parsable integer.
*/
public static int parseInt(String s) throws NumberFormatException {
return parseInt(s,10);
}
重点来哦,String能转为int的本质,这里需要传一个核心参数,Integer给我们默认了10进制,其他类型也是相同,比如Long
public static long parseLong(String s) throws NumberFormatException {
return parseLong(s, 10);
}
因为转换后的就是10进制的数字,可供使用。
进一步分析parseInt(String s, int radix);radix即进制的意思。
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.
*/
//字符串不能为null,没有判断空字符串
if (s == null) {
throw new NumberFormatException("null");
}
//进制不能小于2,至少要2进制
if (radix < Character.MIN_RADIX) {
throw new NumberFormatException("radix " + radix +
" less than Character.MIN_RADIX");
}
//进制不能大于36
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);
//这里玩了个计谋,0字符的ASCII是48,后面的数字包括ABCDEF的ASCII都比0大;
//其中 + 43; - 45
//只有带符号位的会判断,其他就默认正数
if (firstChar < '0') { // Possible leading "+" or "-"
//负数
if (firstChar == '-') {
negative = true;
limit = Integer.MIN_VALUE;
//非负即正,因为小于'0'
} 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
// 拿到字符,转换为ASCII数字并按进制转为数字
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;
}
这里说一下Character.digit(s.charAt(i++),radix)
public static int digit(char ch, int radix) {
return digit((int)ch, radix);
}
digit就是数字的意思,这里直接把char字符强转int类型,即ASCII数字,然后按照进制转换成相应的数字
总结
类型转换其实是字符的ASCII的解析符号位,并按字符转为数字,然后使用逆向负进位方式生成数字,最后修正符号得出我们想要的结果。算法符合了字符串解析的顺序,不符合人类的思维习惯。
本文地址:https://blog.csdn.net/fenglllle/article/details/108857289