本帖最后由 001可望成功 于 2014-7-14 10:39 编辑
这几天看到有些小伙伴问这方面的问题
例如①:
int x=1;
Integer y = 1;
Integer z = new Integer(1);
System.out.println(x==y);
System.out.println(x==z);
System.out.println(y==z);
得到的结果:
true
true
flase
例如②:
String str1 = "a";
String str2 = "b";
String str3 = "ab";
String str4 = str1 + str2;
String str5 = new String("ab");
System.out.println(str5.equals(str3));
System.out.println(str5 == str3);
System.out.println(str5.intern() == str3);
System.out.println(str5.intern() == str4);
得到的结果:
true
false
true
false
下面就先说一下堆、栈、常量池:
在java的内存中主要分为四个部分:栈,堆,数据区(有人说这就是常量池),代码段(存放所有的方法,被线程共享,对象被实例化的时候,都有自己的成员变量,而成员方法是共享的)
- 栈:存放所有的基本类型变量和对象的引用变量。(注意,栈里的数据不是共享的,int a = 3;3存在a的那块内存里,int b = 3,这里会开辟出一个b的内存,存放b的3。)
- 堆:存放所有由new产生的对象或数组,
- 常量池:虚拟机必须为每个被装载的类型维护一个常量池。常量池就是该类型所用到常量的一个有序集和,包括直接常量(string,integer和floating point常量)和对其他类型,字段和方法的符号引用。说白了,就是存放基本类型常量和字符串常量的。对于String常量,它的值是在常量池中的。而JVM中的常量池在内存当中是以表的形式存在的,对于String类型,有一张固定长度的CONSTANT_String_info表用来存储文字字符串值,注意:该表只存储文字字符串值,不存储符号引用。
- 主要用于String和8大基本数据类型(这8个里Float和Double没有用到常量池)的包装类Integer,Long,Short,Character,Byte,Booleans.(包装类的数据范围是-128~127,超过了这个范围,就会在堆中新建这个对象,并不会把这对象存在常量池中了,所以,Integer o = 128;Integer i= 128;o就不等于i了)
接下来说一下上面的各种情况:
int i = 1;基本数据类型的值就存在栈中开辟的内存里,所以,基本数据 类型只在栈中开辟一块内存
Integer i2 = 1;他将基本数据类型自动装箱变为了引用数据类型,这里的1也被存到了常量池中了,他在存之前会先检测一下常量池中有没有1这块内存,如果已经开辟过这块内存了,那么,JVM直接将这块内存的地址赋给i2,如果没有的话,则开辟一块新内存存放1。既然是引用数据类型,那i2就是一个地址了,指向常量池中的某块内存,这是就以有人问了,既然i2是一个地址,那判断i==i2是不就输出false了吗?其实不是这样的,我们平时使用==判断的时候都是相同的数据类型,而这里i是一个基本数据类型,i2是一个引用数据类型,这是JVM会先将Integer拆箱转换为基本数据类型,即int i2 = 1;然后在进行比较,这样i和i2不就一样了嘛
Integer i3 = new Integer(1);一看是new,那他肯定是在堆中强制性的开辟一块内存了,不管堆内存中有没有1这块内存都会开辟。
String i4 = “abc”;这里的i4和i可就不一样了,他不是基本数据类型,而是一个引用数据类型,对于String他比较特殊,因为他也实现了常量池,所以这里的abc也被存到了常量池里了。
String i5 = new String(“abc”);既然new了,那肯定是存在堆内存中了,但是这里有个小陷阱:记得看过一个面试题,他问String i5 = new String(“abc”);是开辟了一块内存还是两块,这里你不管回答一块还是两块都不一定对,因为new String()他会强制性的在堆内存中开辟一块内存,“abc”他也是一个对象啊,JVM会现在常量池中找有没有abc这样的字符串,如果有,直接将这个对象拷贝到堆内存中,这样之开辟一快内存,如果没有,在常量池中新建这个对象,这样的话,开辟两块内存
特别注意的是:常量池是在编译的时候开辟内存,堆是在运行的时候开辟内存
String str1 = "a";
String str2 = "b";
String str3 = "ab";
String str4 = str1 + str2;
String str5 = "a"+"b";
System.out.println(str3==str4);
System.out.println(str3==(str1+str2));
System.out.println(str3==str5);
System.out.println(str3=="a"+"b");
得到的结果是:
false
false
true
true
字符串进行相加的时候,如果都是静态字符串,如“a”+“b”,那么组合起来的字符串存放在常量池中,在存之前先会在常量池中查找,这里不多说;如果字符串相加的时候其中有变量,如str1+“a”,str1+str2等,这样JVM会在堆内存中新建一个对象,并不会在常量池中存放。
下面时候一下String里的intern()方法,这是上面的例子:
String str1 = "a";
String str2 = "b";
String str3 = "ab";
String str4 = str1 + str2;
String str5 = new String("ab");
System.out.println(str5.equals(str3));
System.out.println(str5 == str3);
System.out.println(str5.intern() == str3);
System.out.println(str5.intern() == str4);
得到的结果:
true
false
true
false
在调用str5.intern()方法的时候,这个方法会首先检查字符串池中是否有str5这个字符串,如果存在则返回这个字符串的引用,否则就将这个字符串添加到字符串池中,然会返回这个字符串的引用。
上面的前两个不多说,第三个str5.intern()会先在常量池中查找str5,这里返回了一个引用,就是str3,所以相等
第四个str5.intern()返回的引用是str3,str4是由两个变量组合起来的,上面已经说过,他是在堆内存中开辟的内存,不存放在常量池里,所以两个不相等。
参考资料:
对java中int和Integer的理解 http://blog.sina.com.cn/s/blog_7f033dcf01017ljx.html
java内存分析全面解析 http://blog.csdn.net/shimiso/article/details/8595564
java中的堆、栈、常量池 http://blog.csdn.net/config_man/article/details/5192059
java string = new string 内存分配问题 字符串常量池栈 堆 http://baidongxu.blog.163.com/blog/static/1747323522011924142226/
|