new Integer(18) 与 Integer.valueOf(18) 的区别是什么?”
new Integer(18)每次都会新建一个对象;Integer.valueOf(18)会使⽤用缓存池中的对象,多次调用只会取同⼀一个对象的引用。
来看下面这段代码:
Integer x = new Integer(18);
Integer y = new Integer(18);
System.out.println(x == y);
Integer z = Integer.valueOf(18);
Integer k = Integer.valueOf(18);
System.out.println(z == k);
Integer m = Integer.valueOf(300);
Integer p = Integer.valueOf(300);
System.out.println(m == p);
来看一下输出结果吧:
false
true
false
“第一个 false,因为 new 出来的是不同的对象,地址不同。”
“第二个和第三个我认为都应该是 true 啊,为什么第三个会输出 false 呢?”
“其实原因也很简单。”
基本数据类型的包装类除了 Float 和 Double 之外,其他六个包装器类(Byte、Short、Integer、Long、Character、Boolean)都有常量缓存池。
- Byte:-128~127,也就是所有的 byte 值
- Short:-128~127
- Long:-128~127
- Character:\u0000 - \u007F
- Boolean:true 和 false
拿 Integer 来举例子,Integer 类内部中内置了 256 个 Integer 类型的缓存数据,当使用的数据范围在 -128~127 之间时,会直接返回常量池中数据的引用,而不是创建对象,超过这个范围时会创建新的对象。
18 在 -128~127 之间,300 不在。
来看一下 valueOf 方法的源码吧。
public static Integer valueOf(int i) {
if (i >=IntegerCache.low && i <=IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
“哦,原来是因为 Integer.IntegerCache 这个内部类的原因啊!”
“是滴。来看一下 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 Integer.IntegerCache.high >= 127;
}
private IntegerCache() {}
}
静态代码块通常用来初始化一些静态变量,它会优先于 main() 方法执行。
在静态代码块中,low 为 -128,也就是缓存池的最小值;high 默认为 127,也就是缓存池的最大值,共计 256 个。
可以在 JVM 启动的时候,通过 -XX:AutoBoxCacheMax=NNN 来设置缓存池的大小,当然了,不能无限大,最大到 Integer.MAX_VALUE -129
之后,初始化 cache 数组的大小,然后遍历填充,下标从 0 开始。
“这段代码不难理解,难理解的是 assert Integer.IntegerCache.high >= 127;,这行代码是干嘛的呀?”
assert 是 Java 中的一个关键字,寓意是断言,为了方便调试程序,并不是发布程序的组成部分。
默认情况下,断言是关闭的,可以在命令行运行 Java 程序的时候加上 -ea 参数打开断言。
来看这段代码。
public class AssertTest {
public static void main(String[] args) {
int high = 126;
assert high >= 127;
}
}
假设手动设置的缓存池大小为 126,显然不太符合缓存池的预期值 127,结果会输出什么呢?
直接在 Intellij IDEA 中打开命令行终端,进入 classes 文件,执行:
/usr/libexec/java_home -v 1.8 --exec java -ea com.itwanger.s51.AssertTest
我用的 macOS 环境,装了好多个版本的 JDK,该命令可以切换到 JDK 8
也可以不指定 Java 版本直接执行(加上 -ea 参数):
java -ea com.itwanger.s51.AssertTest
“呀,报错了呀。”
Exception in thread "main" java.lang.AssertionError
at com.itwanger.s51.AssertTest.main(AssertTest.java:9)
“是滴,因为 126 小于 127。”我回答道。
“原来 assert 是这样用的啊,我明白了。”
“那,缓存池之所以存在的原因也是因为这样做可以提高程序的整体性能,因为相对来说,比如说 Integer,-128~127 这个范围内的 256 个数字使用的频率会高一点。”我总结道。