引用自这位朋友: http://blog.sina.com.cn/s/blog_6a6b14100100zn6r.html
你知道在 java 中除了 8 中基本类型外, 其他的都是类对象以及其引用. 所以 "xyz" 在 java 中它是一个 String 对象. 对于 string 类对象来说他的对象值是不能修改的, 也就是具有不变性.
看:
- String s= "Hello";
- s= "Java";
- String s1= "Hello";
- String s2=new String("Hello");
啊, s 所引用的 string 对象不是被修改了吗? 之前所说的不变性, 去那里了啊?
你别着急, 让我告诉你说发生了什么事情:
在 jvm 的工作过程中, 会创建一片的内存空间专门存入 string 对象. 我们把这片内存空间叫做 string 池.
String s= "Hello"; 当 jvm 看到 "Hello", 在 string 池创建 string 对象存储它, 并将他的引用返回给 s.
s= "Java", 当 jvm 看到 "Java", 在 string 池创建新的 string 对象存储它, 再把新建的 string 对象的引用返回给 s. 而原先的 "Hello" 仍然在 string 池内. 没有消失, 他是不能被修改的.
所以我们仅仅是改变了 s 的引用, 而没有改变他所引用的对象, 因为 string 对象的值是不能被修改的.
String s1= "Hello";jvm 首先在 string 池内里面看找不找到字符串 "Hello", 找到, 返回他的引用给 s1, 否则, 创建新的 string 对象, 放到 string 池里. 这里由于 s= "Hello" 了, 对象已经被引用, 所以依据规则 s 和 s1 都是引用同一个对象. 所以 s==s1 将返回 true.(==, 对于非基本类型, 是比较两引用是否引用内存中的同一个对象)
String s2=String( "Hello");jvm 首先在 string 池内里面看找不找到字符串 "Hello", 找到, 不做任何事情, 否则, 创建新的 string 对象, 放到 string 池里面. 由于遇到了 new, 还会在内存上 (不是 string 池里面) 创建 string 对象存储 "Hello", 并将内存上的(不是 string 池内的)string 对象返回给 s2. 所以 s==s2 将返回 false, 不是引用同一个对象.
好现在我们看题目:
String s = new String( "xyz");
首先在 string 池内找, 找到? 不创建 string 对象, 否则创建, 这样就一个 string 对象
遇到 new 运算符号了, 在内存上创建 string 对象, 并将其返回给 s, 又一个对象
所以总共是 2 个对象
一个例子:
- public class Test
- {
- public static void main(String [] args)
- {
- String s1=new String("test");// 创建 2 个对象, 一个 Class 和一个堆里面
- String s2="test";// 创建 1 个对象, s2 指向 pool 里面的 "test" 对象
- String s3="test";// 创建 0 个对象, 指向 s2 指想 pool 里面的那个对象
- String s4=s2;// 创建 0 个对象, 指向 s2,s3 指想 pool 里面的那个对象
- String s5=new String("test");// 创建 1 个对象在堆里面, 注意, 与 s1 没关系
- System.out.println(s2=="test");//true s2=="test" 很明显 true
- System.out.println(s2==s3);//true, 因为指向的都是 pool 里面的那个 "test"
- System.out.println(s2==s4);//true, 同上, 那么 s3 和 s4...:)
- System.out.println(s1==s5);//false, 很明显, false
- System.out.println(s1==s2);//false, 指向的对象不一样, 下面再说
- System.out.println(s1=="test");//false, 难道 s1!="tset"? 下面再说
- System.out.println("---------------");
- s1=s2;
- System.out.println(s1=="test");//true, 下面说
- }
- }
说明: 1,System.out.println(s1==s2); 很明显, s2 指向的对象 "test" 是在 pool 里面, 而 s1 指向的是堆里面的 "test" 对象(s1 指向的内存区), 所以返回 false.
2,System.out.println(s1=="test");s1 指向的是堆里面的 "test" 对象(s1 指向的内存区), 而 "test" 是程序 刚刚建立的(其实是共用 pool 里面的那个已经创建了的 "test" 对象, 也就是我们 s2="test" 时候, 在 pool 里面创建的), 所以 s1 指向的堆 里的 "test" 对象
和 "test"(pool 里面)并不是一样个对象, 所以返回 false.
3, 当我们 s1=s2; 的时候, 很明显, 把 s2 的指给了 s1,s1 指向 pool 里面的 "test", 这个时候, s2 也指向了 pool 里面的 "test" 对 象了, 当 System.out.println(s1=="test"); 时候, java 虚拟机创建 "test" 对象, 注意, 其实没创建, 和前面讲的一 样, 公用 s1="test" 创建的 "test" 对象(pool 里面的), 所以, s1=="test"(pool 里面的), 同 样, s1=s2=s3=s4!
而为什么在网上都说 String s=new String("test"); 创建了 2 个对象? 那可能因为它就写这么一句代码, 误让人默认的认为执行代码之前并不实例任何一个 String 对象过
(也许 很多人不会这么想,), 跟着别人或者不经思考的就说 2 个, 斟是说存放在栈内存中专门存放 String 对象引用的 s 变量是一个对象! 实在不可原谅!
所以当有面试官问起, String s2=new String( "Hello"); 创建几个对象时, 正确答案应该回答, 视情况而定, 当常量池中没有 "Hello" 对象时是创建两个, 一个存放在常量池中, 另一个存放在堆 (内存) 中.
来源: http://www.bubuko.com/infodetail-2693604.html