专业编程基础技术教程

网站首页 > 基础教程 正文

Integer源码分析——上 (jdk11)

ccvgpt 2024-08-06 12:36:26 基础教程 11 ℃



Integer源码分析——上 (jdk11)

  • Integer简述

Integer是JDK1.5出现的int原生类型的包装类型,在JDK9之前版本,通常是使用构造函数的方式创建对象:

Integer integer = new Integer(1); 

从JDK9开始之后,通过构造方法这种方式创建Integer对象的方式被抛弃,使用Integer提供的静态方法来创建对象:

Integer integer = Integer.valueOf(1);

Integer为用户创建integer对象提供了基于int和String的两种类型的方式:

下面主要讨论的基于jdk11的源码进行分析。

  • Integer基本属性
//Integer可以装箱的int最小值,采用16进制表示
@Native public static final int   MIN_VALUE = 0x80000000;
//Integer可以装箱的int最大值
@Native public static final int   MAX_VALUE = 0x7fffffff;
//实际保存原生int的值
private final int value;
//表示的Integer所表示的int类型占32位
@Native public static final int SIZE = 32;
//表示int的字节数
public static final int BYTES = SIZE / Byte.SIZE;
//所有的数子和字母字符
static final char[] digits = {
        '0' , '1' , '2' , '3' , '4' , '5' ,
        '6' , '7' , '8' , '9' , 'a' , 'b' ,
        'c' , 'd' , 'e' , 'f' , 'g' , 'h' ,
        'i' , 'j' , 'k' , 'l' , 'm' , 'n' ,
        'o' , 'p' , 'q' , 'r' , 's' , 't' ,
        'u' , 'v' , 'w' , 'x' , 'y' , 'z'
    };
  • Integer.valueOf(int i)

下面先看源码:

@HotSpotIntrinsicCandidate
public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
          return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

根据上面的源码,我们可以看到出现了一个IntegerCache类,该类的作用是缓存一部分int类型的自动装箱的对象值,默认情况下是缓存-128到127之间256个数字,也就是说,在jvm中无论调用几次Integer.valueOf(1),最后只会在jvm中存在一个对象。这个缓存的最大值可以在启动jvm的时候置,通过-XX:AutoBoxCacheMax=<size>进行设置。下面具体看一下IntegerCache类的实现:

private static class IntegerCache {
        static final int low = -128;//定义了缓存的最小值
        static final int high; //用来存储最大的缓存数值
        //保存jvm创建的所有的范围在low和high之间的对象
        static final Integer cache[];

        static {
            // 默认是127
            int h = 127;
            //如果启动JVM的时候设置了-XX:AutoBoxCacheMax参数,那么改参数值会保存到
            //java.lang.Integer.IntegerCache.high此环境变量中
            String integerCacheHighPropValue =
                VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    //把字符串转换为数字
                    int i = parseInt(integerCacheHighPropValue);
                    //户设置的新的最大int值应该大于127
                    i = Math.max(i, 127);
                    //此行的目的确保最大值小于Integer.MAX_VALUE - (-low) -1
                    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;
            //创建保存缓存对象的数组
            cache = new Integer[(high - low) + 1];
            int j = low;
            //缓存中的对象会被全部创建出来,而不是在用到某一个再去创建某一个
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

           
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }

上面的代码中h = Math.min(i, Integer.MAX_VALUE - (-low) -1);有的人可能不理解,下面重点解释一下。IntegerCache通过它内部定义的数组来保存Integer的缓存对象。在Java中数组的最大元素个数是Integer.MAX_VALUE,由于IntegerCache还会保存128个复数和0。因此IntegerCache数组大小:

cache.size = 128+1+h(最大正数)

所以h的最大值是Integer.MAX_VALUE - (-low) -1

由上面的源码可知,当IntegerCache第一次被使用的时候,jvm就会创建所有的在缓存范围内的Integer对象。

下面的代码表示当我们需要创建的Integer对象不再缓存的范围内,才会去调用构造方法去创建新对象

 if (i >= IntegerCache.low && i <= IntegerCache.high)
          return IntegerCache.cache[i + (-IntegerCache.low)];
 return new Integer(i);

至于Integer的构造方法比较简单:


@Deprecated(since="9")
public Integer(int value) {
        this.value = value;
}
  • valueOf(String s)

此方法的具体实现如下:

public static Integer valueOf(String s) throws NumberFormatException {
    return Integer.valueOf(parseInt(s, 10));
}

首先调用parseInt方法把传入的字符串转换为int值,然后再调用上面提到的valueOf(int i)方法。

下面介绍一下parseInt的实现原理:

public static int parseInt(String s, int radix)
            throws NumberFormatException
{
    
  boolean negative = false;
    int i = 0, len = s.length();
    int limit = -Integer.MAX_VALUE;

    if (len > 0) {
        char firstChar = s.charAt(0);
        //判断一个字符是不是是 '-'
        if (firstChar < '0') {
            if (firstChar == '-') {
                negative = true;
                limit = Integer.MIN_VALUE;
            } else if (firstChar != '+') {
                throw NumberFormatException.forInputString(s);
            }

            if (len == 1) { 
                throw NumberFormatException.forInputString(s);
            }
            i++;
        }
        int multmin = limit / radix;
        int result = 0;
        //避免溢出
        while (i < len) {
            int digit = Character.digit(s.charAt(i++), radix);
            if (digit < 0 || result < multmin) {
                throw NumberFormatException.forInputString(s);
            }
            result *= radix;
            if (result < limit + digit) {
                throw NumberFormatException.forInputString(s);
            }
            result -= digit;
        }
        return negative ? result : -result;
    } else {
        throw NumberFormatException.forInputString(s);
    }
}

在parseInt方法的主要实现原理就是,从左到右获取字符串中的每个数字字符,将数字字符转换为对应的数字:

int digit = Character.digit(s.charAt(i++), radix);

然后把上一轮转换的结果进行扩展(result是保存每一数字处理后的结果):

result *= radix;//一般转换的是十进制数据,因此radix=10

然后把加上result的值加上本次获取digit,也就是说正常的逻辑是:

比如转换的字符为:str="3425"

1.因为str长度为4,因此需要循环4次,第一次获取第一个数字为3,本次循环结果result=3

2.第二次循环,获取到的数字为4,然后计算本次循环结果result = 3*10 +4,result = 34

3.第三次循环,获取到的数字为2,然后计算本次循环结果result = 34*10 +2,result = 342

4.第四次循环,获取到的数字为5,然后计算本次循环结果result = 342*10 +5,result = 3425

作者这里却用到了一种特殊的方式,就是使用负数的方式:

result -= digit;

这样做的目的就是为了预付需要转换的数字字符串出现溢出:

//下面的代码当出现上溢或者下溢的时候执行  例如需要转换的字符串为:"-21474836490"或者"21474836490"
if (result < limit + digit) {
   throw NumberFormatException.forInputString(s);
}

最近发表
标签列表