前言
String是我们接触和使用最多的,也是最基础的Java对象,相信大家都不陌生,我们先来看两个小问题:
1.new String(“abc”)创建了几个对象?
2.String s = “abc” ; String s1 = “ab” + “c”; System.out.println(s==s1);输出结果是什么?
所以答案是什么呢,见下分晓
字符串创建和存储的机制
字符串的初始化有两种情况:
- first
对于String s1 = new String(“abc”) 和String s2 = new String(“abc”)存在两个引用对象s1、s2,两个内容相同的字符串对象"abc",但它们在内存中的地址是不同的。所以只要用new总会生成新的对象。
- second
对于String s1 = "abc"和String s2 = “abc”,在JVM中存在着一个字符串池,其中保存着很多String对象,并且可以被共享使用,s1、s2引用的是同一个常量池中的对象。
由于String的实现采用了Flyweight的设计模式,当创建一个字符串常量时,会首先在字符串常量池中查找是否已经有相同的字符串被定义,采用equal()方法来判断。若已经定义,则直接获取对其的引用,此时不需要创建新的对象;若没有定义,则首先创建这个对象,然后把它加入到字符串池中,再将它的引用返回。
example:
String s1 = "abc"; //在常量区里面存放一个"abc"字符串对象
String s2 = "abc"; //s2引用常量区中对象,因此不会创建新的对象
String s3 = new String("abc"); //在堆中创建新的对象
String s4 = new String("abc"); //在堆中又创建了一个新的对象
为了方便理解,可以把String s = new String(“abc”)的执行人为的分为两个过程:
第一个过程是新建对象的过程,即new String(“abc”);
第二个过程是赋值的过程,即String s=。
由于第二个过程只是赋值过程,不会创建新的对象,所以只有第一个过程new String(“abc”)会调用String类的构造函数。
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
在调用构造函数时传入了一个字符串常量,因此new String(“abc”)也就等价于"abc"和new String()两个操作了。
若字符串池中不存在"abc",则会创建一个字符串常量"abc",并添加到字符串池中;
若存在,则不创建,然后new String()会在堆中创建一个新的对象。
从上面的分析可以回答上面两个问题
1、new String(“abc”)创建了几个对象?
答案:一个或两个。如果常量池中原来有"abc",那么只需要创建一个对象;如果没有,那么就会创建两个对象。
2、String s = “abc” ; String s1 = “ab” + “c”; System.out.println(s==s1);输出结果是什么?
答案:true。因为"ab"+“c"在编译器就被转换为"abc”,存放在常量区,因此s1和s2引用了同一对象。
引申思考:
String类型的变量s,赋值语句s == null与s= ""是否相同?
小结
String初始化存在两种方式,String s="abc"和String s = new String(“abc”),创建s对象时会先通过equal()方法判断字符串常量池内是否存在该对象。如果存在直接引用,不存在需要创建新的对象,同时new比如会创建一个新的对象。