一个看似很简单的问题:当String s = new String("kevin");后内存中有几个对象?
通常回答分两类,一个对象和两个对象。有关两个对象的错误认识是s 和kevin。但是情况似乎并不是那样简单!我们做个测试。我们知道java中 == 比较的是对象的引用是不是相同,就是比较是不是指向同一个对对象,也就是比较是不是同一个对象。我们据此写一小段测试代码:
package com.ys.String;
public class StringTest {
public static void main(String[] args) {
String s0 = new String ("kevin");
String s1 = "kevin";
String s2 = new String ("kevin");
String s3 = "kevin";
if(s0 == s1 ){
System.out.println("s0和s1指向相同的对象");
} else {
System.out.println("s0和s1指向不同的对象");
}
if(s1 == s2) {
System.out.println("s2和s1指向相同的对象");
} else {
System.out.println("s2和s1指向不同的对象");
}
if(s0 == s2) {
System.out.println("s2和s0指向相同的对象");
} else {
System.out.println("s2和s0指向不同的对象");
}
if(s3 == s1 ){
System.out.println("s3和s1指向相同的对象");
} else {
System.out.println("s3和s1指向不同的对象");
}
System.out.println(s0 == s1); //false
System.out.println(s1 == s2); //false
System.out.println(s0 == s2); //false
System.out.println(s1 == s3); //true
System.out.println(s0.equals(s1)); //true
System.out.println(s0.equals(s2)); //true
System.out.println(s0.equals(s1)); //true
System.out.println(s0.equals(s1)); //true
}
}
输出结果如下:
s0和s1指向不同的对象
s2和s1指向不同的对象
s2和s0指向不同的对象
s3和s1指向相同的对象
false
false
false
true
true
true
true
true
由此我们可以得出结论:上面的s0 s1 s2他们指向了不同的对象,他们是不同的对象引用。而s1 s3是相同的对象引用。但是这仍然没有能够回答我们的问题,到底几个对象生成了?造成s0 s1 s2指向不同对象的原因何在?我们需要大概了解java中有关String的内存分配原理。
当执行String s0 = new String("kevin")的时候,会为其创建一个字符串缓冲池(pool)。当创建一个字符串对象的时候其执行流程是这样的:首先他到pool中去寻找是否有一个叫内容为kevin的这样的一个对象存在。因为String s0 = new String("kevin")是main方法的第一条语句此时还没有pool中是没有对象的。此时他会把括号中的kevin对象放到pool中。接下来执行构造方法在堆(heap)中生成一个内容为kevin的对象。所以这样就造成了pool 和 heap中都有一个内容为kevin的对象。这样就解答了我们的问题。我们知道通过 String s0 = new String("kevin") 后,其实产生了两个对象。通过最后四行的代码我们也可以清楚的知道。就是说java中 == 比较的是对象的引用 equals()方法比较的是对象的内容是不是相同。
我们同时注意到在执行 String s1 = "kevin" 后不会有新的对象产生。他首先会到pool中去寻找是否有一个内容为kevin的对象存在,这里找到这样一个对象。他就把引用s1执行pool中的这个对象。所以不会有新的对象产生。同理我们知道s3 和s1指向pool中的同一对象。而执行 String s2 = new String ("kevin") 时,他首先查看pool,发现有一个这样一个内容为kevin的对象存在,那么就不必在pool中创建这样一个对象了。接着他执行构造方法在堆中创建一个内容为kevin的对象,由s2指向。我们要注意的尽管堆中有两个内容为kevin的对象,他们仅仅是内容相同而已,他们的引用是不想同的。
同样的问题如下:
package com.ys.String;
public class StringTest {
public static void main(String[] args) {
String s1 = "kevin";
String s2 = "ke";
String s3 = "vin";
System.out.println(s1 == "ke" + "vin");
System.out.println(s1 == "ke" + s3);
}
}
输出会是如何呢?
true
false
产生这样的原因是:当 + 两边都是字面值(常量)的时候,其连接成一个字符串。在到pool中看是否存在一个内容为kevin的对象。本例中存在,所以返回true;而当 + 号两边有一边不是字面值的时候其会在堆中分配空间。即是说:s1在pool中而“ke” + s3 在堆中。所以返回false。