Java Integer常量池
在Java中我们知道String有一个常量池,维护了所有的String对象。我们写String temp="test"的时候其实是使用String.valueOf("test")从常量池中找了一个对象返回,但是如果使用String temp=new String("temp")的话就不会从常量池中去找,而是直接创建一个新的对象。
其实对于Integer来说也有这样一个常量池存在,并且我们都知道String常量池自从HotSpot1.8之后就从方法区移动到堆中了,而Integer常量池在哪里基本上都没有人提到,我个人比较相信它也是在堆中的。之所以讨论的少可能是因为它相对不太重要吧
https://blog.csdn.net/womenghuangliang/article/details/17377285
Integer i = 10 和 Integer j = new Integer(10) 的区别
public class IntegerClassDemo { public static void main(String[] javalatte) { Integer i = 10; Integer j = new Integer(10); Integer k = 145; Integer p = new Integer(145); } }
在编译了上面这个类之后,我尝试着反编译这个类的 Class 文件。我得到了下面这个类
public class IntegerClassDemo { public static void main(String[] javalatte) { Integer i = Integer.valueOf(10); Integer j = new Integer(10); Integer k = Integer.valueOf(145); Integer p = new Integer(145); } }
所以当我们用 Integer i = 10 的方式创建一个 Integer 类时,Java 调用了方法 Integer.valueOf()。
Integer.valueOf()
让我们来看看这个函数做了什么。JDK 文档是这样说的:
public static Integer valueOf(int i)
返回一个表示指定的 int 值的 Integer 实例。如果不需要新的 Integer 实例,则通常应优先使用该方法,而不是构造方法 Integer(int),因为该方法有可能通过缓存经常请求的值而显著提高空间和时间性能。
这个方法总是缓存了 - 128 到 127 之间的值,还有,它也可以缓存这个范围之外的值。
为了说明这个问题,让我们来看看 Integer.valueOf() 的源代码。
public static Integer valueOf( int i) { assert IntegerCache. high >= 127; if (i >= IntegerCache. low && i <= IntegerCache. high ) return IntegerCache. cache[i + (-IntegerCache. low)]; return new Integer(i); }
通过上面的代码,我们可以很清楚的看到,当我们在 - 128 到 127 这个范围内调用 valueof() 这个函数时,实际上使用的是缓存(IntegerCache)里面的值。
IntegerCache 是帮助我们缓存 Integer 的类。缓存数值的大小是由 - XX:AutoBoxCacheMax=
来控制,或者可以通过系统属性来获得:-Djava.lang.Integer.IntegerCache.high= 所以如果值在 - 128 到 127 之间,你使用 valueof() 将获得相同的引用,超过这个范围将返回 new Integer(int)。因为是相同的引用,在 - 128 到 127 范围内你的 == 操作是有用的。
Java 缓存了 - 128 到 127 范围内的 Integer 类。所以当你给这个范围的封装类赋值时,装箱操作会调用 Integer.valueOf() 函数,反过来就会给封装类赋上一个缓存池实例的引用。
另一方面来说,如果用超出这个范围的值给封装类赋值时,Integer.valueOf() 会创建一个新的 Integer 类。因此,比对超出这个范围的 Integer 类时,会返回 false。
也就是说,Integer常量池一定缓存了-128到127之间的对象,因为这是一个字节表示的范围内的数,也是最常用的数,缓存这些就够了,过多的话会导致内存占用过大且没什么用。而要使用缓存,就不能使用new Integer()的方式,它是不走常量池的。
至于为什么只缓存-128到127而字符串就是全部缓存,我感觉原因是字符串的范围比Integer小:我们可能使用任何值的Integer对象,但是只会使用我们创建过的字符串对象,所以一个程序中字符串的范围是比整数小的多的,而是复用的概率也高。
而对于包装类,我们要注意==和equals的区别:
@Test public void testEquals() { int int1 = 12; int int2 = 12; Integer integer1 = new Integer(12); Integer integer2 = new Integer(12); Integer integer3 = new Integer(127); Integer a1 = 127; Integer a2 = 127; Integer a = 128; Integer b = 128; System.out.println("int1 == int2 -> " + (int1 == int2)); System.out.println("int1 == integer1 -> " + (int1 == integer1)); System.out.println("integer1 == integer2 -> " + (integer1 == integer2)); System.out.println("integer3 == a1 -> " + (integer3 == a1)); System.out.println("a1 == a2 -> " + (a1 == a2)); System.out.println("a == b -> " + (a == b)); }
答案是: 1、 int1 == int2 -> true 2、 int1 == integer1 -> true 3、 integer1 == integer2 -> false 4、 integer3 == a1 -> false 5、 a1 == a2 -> true 6、 a == b -> false
- int和int用==是比值
- Integer和int用==是比值
- Integer和Integer用==是比引用
我个人建议如果要使用包装类Integer的话还是不要冒险,即使知道有常量池的存在也在比较中只使用equals(),否则容易出Bug