Java Integer的内存存储在堆和常量池中,及String
先看代码:
int i1 = 128;
Integer i2 = 128;
Integer i3 = new Integer(128);
//Integer会自动拆箱为int,所以为true
System.out.println(i1 == i2);
System.out.println(i1 == i3);
System.out.println(i2 == i3);
System.out.println("**************");
Integer i4 = 127;//java在编译的时候,被翻译成-> Integer i5 = Integer.valueOf(127);
Integer i5 = 127;
Integer i6 = Integer.valueOf(127);
System.out.println(i4 == i5);//true
System.out.println(i4 == i6);//true
Integer i7 = new Integer(127);
System.out.println(i4 == i7); //false
Integer i8 = 128;
Integer i9 = 128;
System.out.println(i8 == i9);//false
Integer i10 = new Integer(128);
Integer i11 = new Integer(128);
System.out.println(i10 == i11); //false
测试的结果:
Paste_Image.png在Java中,对于对象==是比较两个对象的地址。
Integer的存储
1、Integer是int的封装类,当基础变量(int)和Integer进行比较时,Integer会自动拆箱(jdk1.5以上)了后,再去和int进行比较,所以判断i1==i2,i1==i3都为true。
2、对JVM为了节省空间, 当Integer的值落在-128~127之间时,如i4,i5;此时JVM首先检查是否已存在值为127的Integer对象。如果是,则i4,i5直接是引用已存在对象,即i4 = i5。所以判断i4 == i5 为 true
那为什么范围是-128到127呢:java在编译Integer i4 = 127的时候,是被翻译成-> Integer i4 = Integer.valueOf(127)的;这就是判断i4==i6为true的原因了。接下来,关键就是看valueOf()函数了。只要看看valueOf()函数的源码就会明白了。
Paste_Image.png Paste_Image.png
从代码上看出, 当要赋的值在[-128~127]范围内,则会直接指向该值的引用,不用去new 个对象到堆内存中去了。因为Integer已经缓存了数据。但是,当超出了数组的范围值时,就会去自动装箱在堆内存中建一个新对象。所以,对i8,i9,即使基础变量值一样,封装类对象却指向不同地址。所以判断i8==i9为false
3、对于显式的new Integer(int i),JVM将直接分配新空间。这样两个new Integer(int i)分配的堆内存空间肯定不是同一个,所以判断i10== i11为false。
此外两点, 显式的new Integer(int i)和int自动装箱成的Integer,并不会是同一个对象。他们也是两个不同的存在堆内存中的空间。所以判断i2==i3为false;显式的new Integer(int i)和i范围在[-128~127]内的直接赋值Int类型的值也不指向同一个空间。如判断i4==i7为false,i7指向常量池,而i4指向的是堆内存。
接下来是我的另一个思考:Integer作为对象包装器类,是没有set()方法的。他的值是final的。
Paste_Image.png
那我就是想给他改值怎么办。可以!用反射。
Integer j1 = 2;
Integer j2 = 2;
System.out.println("j1 = j2? " + (j1 == j2)); //true
try {
Field field = Integer.class.getDeclaredField("value");
field.setAccessible(true);
field.set(j2,129);
System.out.println( "j1+"+j1+"+j2+" + j2
+"\nSystem.identityHashCode(j1)"+System.identityHashCode(j1)
+"\nSystem.identityHashCode(j2)"+System.identityHashCode(j2)
+"\nj1 == j2" + (j1 == j2));
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
Integer j3 = 2;
Integer j4 = 2;
System.out.println("j3+"+j3+"\nSystem.identityHashCode(j3)"+System.identityHashCode(j3)+"\nj3 = j4? " + (j3 == j4));
输出结果:
Paste_Image.png
我们可以看出什么呢?常量池里的对应值改变了,所有调用2的值返回的都是129。这是为什么,我画了个图,请看:
当我通过反射修改值时,改变了就是常量池中,IntegerCache.cache[]数组中的值。而我们的下标并没有改变。(这里可能会有人说我常量池与IntegerCache.cache[]数组之间理解有问题,我还没深入研究过,只是想表示是以数组方式存储的)
所以第三步我们所谓的赋值“=”,对应的是常量池中2对应的下标里的值,改成了129。
就好比:int[10] i = {0,1,2,3
,9} 变成了int[10] i = {0,1,129,3
,9} 。因此上面的输出结果没问题,所有对应的Integer里,本来是2的值都变成了129。所以!!!没事别像我一样瞎想。
String的存储
对于使用字面量赋值方式。JVM为了节省空间,会首先查找JVM中是否有对应的字符串常量。如果已经存在,则直接返回该引用,而无需重新创建对象。对象new创建方式,JVM将分配新空间。
String a="1";
String b="1";
int aHashCode = System.identityHashCode(a);
int bHashCode = System.identityHashCode(b);
System.out.print("\na:"+a+"\nb:"+b);
System.out.print("\naHashCode:"+aHashCode+"\nbHashCode:"+bHashCode);
try {
Field value = String.class.getDeclaredField("value");
value.setAccessible(true);
char[] valueChar = (char[]) value.get(b);
valueChar[0] = '2';
String c="1";
String d="2";
int cHashCode = System.identityHashCode(c);
int dHashCode = System.identityHashCode(d);
System.out.print("\na:"+a+"\nb:"+b+"\nc:"+c+"\nd:"+d);
System.out.print("\naHashCode:"+aHashCode+"\nbHashCode:"+bHashCode+"\ncHashCode:"+cHashCode+"\ndHashCode:"+dHashCode);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
对应的输出:
Paste_Image.png一个例子,来自:http://blog.csdn.net/hejingyuan6/article/details/50489171:
String s1 = "china";
String s2 = "china";
String ss1 = new String("china");
String ss2 = new String("china");
int i = 1;
int j = 1;
public static final int i1 = 1;
public static final int j1 = 1;
Integer it1 = 127;
Integer it2 = 127;
Integer it11 = 128;
Integer it12 = 128;
还有一点,就是String的拼接,作用于哪要看虚拟机和编译的jdk版本。我没深入研究,你们看着办吧。反正,对于频繁长拼接,用StringBuffer更好。
最后附上整个class的测试代码:
package com.yy007.zxing;
import java.lang.reflect.Field;
/**
* Created by 仁昌居士 on 2017/6/16.
* Description:
*/
public class TestAcitivity {
/**
* @param args
*/
public static void main(String[] args) {
int i1 = 128;
Integer i2 = 128;
Integer i3 = new Integer(128);
//Integer会自动拆箱为int,所以为true
System.out.println(i1 == i2);
System.out.println(i1 == i3);
System.out.println(i2 == i3);
System.out.println("**************");
Integer i4 = 127;//java在编译的时候,被翻译成-> Integer i5 = Integer.valueOf(127);
Integer i5 = 127;
Integer i6 = Integer.valueOf(127);
System.out.println(i4 == i5);//true
System.out.println(i4 == i6);//true
Integer i7 = new Integer(127);
System.out.println(i4 == i7); //false
Integer i8 = 128;
Integer i9 = 128;
System.out.println(i8 == i9);//false
Integer i10 = new Integer(128);
Integer i11 = new Integer(128);
System.out.println(i10 == i11); //false
System.out.println("**************");
Integer j1 = 2;
Integer j2 = 2;
System.out.println("j1 = j2? " + (j1 == j2)); //true
try {
Field field = Integer.class.getDeclaredField("value");
field.setAccessible(true);
field.set(j2,129);
System.out.println( "j1+"+j1+"+j2+" + j2
+"\nSystem.identityHashCode(j1)"+System.identityHashCode(j1)
+"\nSystem.identityHashCode(j2)"+System.identityHashCode(j2)
+"\nj1 == j2" + (j1 == j2));
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
Integer j3 = 2;
Integer j4 = 2;
System.out.println("j3+"+j3+"\nSystem.identityHashCode(j3)"+System.identityHashCode(j3)+"\nj3 = j4? " + (j3 == j4));
System.out.println("**************");
String a="1";
String b="1";
int aHashCode = System.identityHashCode(a);
int bHashCode = System.identityHashCode(b);
System.out.print("\na:"+a+"\nb:"+b);
System.out.print("\naHashCode:"+aHashCode+"\nbHashCode:"+bHashCode);
try {
Field value = String.class.getDeclaredField("value");
value.setAccessible(true);
char[] valueChar = (char[]) value.get(b);
valueChar[0] = '2';
String c="1";
String d="2";
int cHashCode = System.identityHashCode(c);
int dHashCode = System.identityHashCode(d);
System.out.print("\na:"+a+"\nb:"+b+"\nc:"+c+"\nd:"+d);
System.out.print("\naHashCode:"+aHashCode+"\nbHashCode:"+bHashCode+"\ncHashCode:"+cHashCode+"\ndHashCode:"+dHashCode);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}