引入:new Integer(18) 与 Integer.valueOf(18) 有区别吗?有的话,有什么区别?

我们都知道,使用 new 关键字的时候,每次都会新创建一个对象。但是,Integer.valueOf() 会新创建一个对象吗?

我们来看下面这段代码:

/**
 * new Integer与 Integer.valueOf() 的区别
 *
 * @author qiaohaojie
 * @date 2023/3/4  16:23
 */
public class ValueOfMethod {
    public static void main(String[] args) {
        Integer x = new Integer(18);
        Integer y = new Integer(18);
        System.out.println(x == y); // false

        Integer a = Integer.valueOf(18);
        Integer b = Integer.valueOf(18);
        System.out.println(a == b); // true

        Integer m = Integer.valueOf(300);
        Integer n = Integer.valueOf(300);
        System.out.println(m == n); // false
    }
}

这个结果出现的原因是这样的:

  1. 第一个结果是 false 是因为两次使用 new 时,创建的是两个不同的对象,也就意味着地址不同,所以结果是 false;
  2. 第二个结果是 true 是因为 Integer.valueOf() 方法会使用常量缓存池中的对象,无论调用多少次,都只会取同一个对象的引用;
  3. 第三个结果是 false 是因为 Integer 内置的 256 个缓存数据是在 -128~127 之间,超过这个范围时会创建新的对象。而 300>127 ,所以 m 和 n 会创建两个新的对象,地址也就不同了,所以结果与第一种是一样的 false。

基本数据类型的包装类除了 Float 和 Double之外,其他六个包装类(Byte、Short、Integer、Long、Character、Boolean)都有对应的常量缓存池:

  • Byte:-128~127
  • Short:-128~127
  • Integer:-128~127
  • Long:-128~127
  • Character:\u0000~\u007F
  • Boolean:true、false

接着我们来扒一下 valueOf() 方法的源码:

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

我们可以看到,valueOf() 方法内部主要使用了一个静态内部类 IntegerCache

private static class IntegerCache {
    static final int low = -128;
    static final int high;
    static final Integer cache[];

    static {
        // high value may be configured by property
        int h = 127;
        String integerCacheHighPropValue =
            sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        if (integerCacheHighPropValue != null) {
            try {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                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++);

        // range [-128, 127] must be interned (JLS7 5.1.7)
        assert IntegerCache.high >= 127;
    }

    private IntegerCache() {}
}

我们都知道,用 外加一个大括号括起来的代码被称为静态代码块,通常用来初始化一些静态变量,优先于 main() 方法执行。

在静态代码块中,low 为 -128,也就是缓存池的最小值;high 默认是 127,也就是缓存才能中的最大值,一共是 256 个。

可以在 JVM 启动的时候,通过 -XX:AutoBoxCacheMax=NNN 来设置缓存池的大小,但是不能无限大,最大到 Integer.MAX_VALUE -129。

然后,开始初始化 cache 数组的大小(256),然后开始从下标为 0 开始遍历填充。

最后一行代码 assert IntegerCache.high >= 127 中,assert 是 Java 中的一个关键字,是断言的意思,只是为了方便调试,并不是指发布程序的组成部分。默认情况下,断言是关闭的,我们可以在命令行运行 Java 程序的时候加上 -ea 参数打开断言。

断言的意思可以当成 if…else 语句来理解:


if(假设成立)
{
  程序正常运行;
}
else
{
  报错&&终止程序!(避免由程序运行引起更大的错误)  
}

举个例子:

/**
 * new Integer与 Integer.valueOf() 的区别
 *
 * @author qiaohaojie
 * @date 2023/3/4  16:23
 */
public class ValueOfMethod {
    public static void main(String[] args) {
        int high = 126;
        assert high >= 127 ? true : false;
        System.out.println(false);

        boolean isRight = 1 > 2;
        assert isRight : "程序错误";
        System.out.println("程序正常");
    }
}

输出:
Java中 new Integer 与 Integer.valueOf 的区别-小白菜博客
缓存池之所以使用 assert 是因为这样做可以提高程序的整体性能。因为相比较而言,比如说 Integer,在 128~127 这个范围内的 256 个数字使用的频率相对会高一些。