一、最近总结
最近这几天不用加班,时间比较多,在微信读书阅读《编写高质量代码-改善Java程序的151个建议》这本书,截止现在读到5%左右,给我的感觉就是干货满满,作者以他丰富的经验,将解决问题之后的总结,写入该书之中,让读者收益匪浅呀=-=
二、抛砖引玉
1、请看下面程序,智慧的你,请在控制台分次输入:127、128、555,你会觉得程序运行结果是什么呢?
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
while (input.hasNextInt()) {
int ii = input.nextInt();
System.out.println("\n ==========" + ii + "的相等判断=========");
Integer i = new Integer(ii);
Integer j = new Integer(ii);
System.out.println("new产生Integer对象的比较:" + (i == j));
//基本类型转换为包装类型后的比较
i = ii;
j = ii;
System.out.println("基本类型转换成Integer对象的比较:" + (i == j));
//通过静态方法生成的一个实例
i = Integer.valueOf(ii);
j = Integer.valueOf(ii);
System.out.println("valueOf产生Integer对象的比较:" + (i == j));
}
}
2、运行结果:
输入值 | 程序运行结果 |
---|---|
127 | |
128 | |
555 |
3、问题来了:
对于基本类型转换成Integer对象、和通过valueOf产生Integer对象的比较(可以总结为:
通过装箱生成的对象
)
为什么输入127返回结果都是true,而128、555都是返回false呢
?
4、解答来了:
(1)对于new产生的对象
new声明的就是要生成一个新的对象,没二话,这是两个对象,地址肯定不等,比较结果为false。
(2)通过装箱产生的对象
Integer的装箱动作,是通过valueOf方法实现的,且让我们浏览一下jdk1.8该方法源码吧
valueOf源码解析:
/**
* Returns an Integer instance representing the specified int value.
* If a new Integer instance is not required,
* this method should generally be used in preference to
* the constructor Integer(int), as this method is likely
* to yield significantly better space and time performance by
* caching frequently requested values.
* 翻译:返回表示指定int值的Integer实例。
*
*
* This method will always cache values in the range -128 to 127,
* inclusive, and may cache other values outside of this range.
* 翻译:此方法将始终缓存-128到127(包括端点)范围内的值,并可以缓存此范围之外(需要进行jdk参数设置)的其他值。
*
* @param i an int value.
* @return an {@code Integer} instance representing {@code i}.
* @since 1.5
*/
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
翻译并加上自己的理解:
1、如果不需要产生新的Integer实例,通常应优先使用此方法(优先于使用构造函数Integer(int)),因为这个方法通过缓存频繁请求的值(
整形池
),将空间获得更好的空间和时间性能(显着提高)。
2、此方法将始终缓存-128到127(包括端点)范围内的值,并可以缓存此范围之外(需要设置jdk初始化参数
)的其他值。
3、(1)入参:一个int值 (2) 返回值:返回一个Integer的实例 (3)方法始于jdk1.5
白话文理解:如果要将-128到127(包括断点)的int类型的值转换为Integer对象,默认是从cache缓存数组中获取的(即整形池
)
IntegerCache.cache源码解析:
/**
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage. The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
*/
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// [-128,127]之外的int值,想要每次new Integer(int i)从缓存池中获取,则需要配置jdk初始化参数
// 1、默认最大值是127
int h = 127;
// 2、获取用户设置的jdk初始化high值
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
// 3、如果用户配置了初始化值
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
// 4、从(默认值127、用户自定义值)中取出值最大者
i = Math.max(i, 127);
// 5、从(4中最大值,Integer.MAX_VALUE)中取出最小者
// 注意:在java中,数组的大小(元素数量),最多只能有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;
// 6、初始化缓存池大小
cache = new Integer[(high - low) + 1];
int j = low;
// 6、往缓存池中填数据
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() {}
}
翻译并加上自己的理解:
1、根据java规范JLS(http://java.sun.com/docs/books/jls/): 缓存池支持自动装箱之间的int值为范围-128和127(含)
2、缓存池(整形池)在首次使用时进行初始化。 缓存池的大小可以通过配置{-XX:AutoBoxCacheMax = high值
}进行控制。
在虚拟机初始化期间,java.lang.Integer.IntegerCache.high属性,可以设置并保存在sun.misc.VM类私有系统属性中。
白话文理解就是:缓存池默认缓存的int值范围为:-128到127(包含端点),可以通过设置-XX:AutoBoxCacheMax = value来控制缓存值范围。
三、总结
工作上经常用到的Integer,不看源码还真的不知道有这么多奥妙,这不,看了valueOf的源码之后,得到一个高效的代码技巧:
如果不需要产生新的Integer实例,自动装箱应优先使用 valueOf(int i) ,这将使你写得代码有更好的空间和时间性能
看完这篇文章,你get到了吗???
四、说明:
本博客源码jdk版本为1.8