String类实例化有两种方式:
- 通过直接赋值的形式为String类对象实例化
package com.yao.test;
public class StringTest {
public static void main(String[] args) {
String str= "hello word";
System.out.println(str);
}
}
运行结果:hello word
- 通过构造方法实例化String类对象
package com.yao.test;
public class StringTest {
public static void main(String[] args) {
String str = new String("hello word");
System.out.println(str);
}
}
运行结果:hello word
可以发现,两种方式得到的结果是相同的。但是,两者之间是有本质区别的,下面我们来分析一下:
- 当我们直接采用赋值方式实例化对象时,此时内存会开辟一块堆内存,内存空间将保存有"hello word" 字符串数据,并且栈内存将直接引用此堆内存空间,如下图所示:
当代码中使用了直接赋值的方式定义了一个String类对象时,会将此字符串对象所使用的匿名对象入池保存,如果后续还有其他String类对象也采用了直接赋值的方式,并且设置了同样的内容,那么将不会开辟新的堆内存,而是使用已有的对象进行引用分配,从而继续使用。我们可以看一个例子:
package com.yao.test;
public class StringTest {
public static void main(String[] args) {
String strA = "hello Java";
String strB = "hello Java";
String strC = "hello PHP";
System.out.println(strA==strB);
System.out.println(strA==strC);
}
}
运行结果: true
false
可以看出,使用直接赋值实例化操作方式,而且内容相同,即使没有发生对象的引用操作,最终两个String对象(strA、strB)也都指向了同一块堆内存。
- 当我们使用构造方法实例化String对象时,因为每一个字符串都是一个String类的匿名对象,所以会首先在堆内存中开辟一块空间保存字符串"hello word",而后又使用new关键字开辟另一块堆内存空间,而真正使用的是用new关键字开辟的堆内存,而之前定义的字符串常量开辟的堆内存空间将不会被任何的栈内存所指向,成为垃圾空间,并等待被GC回收。所以,使用构造方法的方式开辟的字符串对象,实际上会开辟两块空间,其中一块空间将成为垃圾,如图所示:
所以,当我们使用构造方式实例化String对象时,除了浪费内存外,还因为new关键字开辟新内存,从而导致其内容不会保存到对象池中。