String的定义生成的对象以及内存的管理一直是我矛盾和纠结的地方。所以写下该文章希望对大家对String对象的创建有所了解。
首先,了解下对象的创建。
String str = new String("abc");
该代码总共创建了两个对象呢?答案是创建了2个。
因为new String("abc")生成字符串的时候,会先去常量池查找是否有该字符串。
- 若是常量池中没有"abc"字符串对象,则在常量池中创建该字符串对象,然后再去堆中创建一个该字符串对象。
- 若是常量池已有该字符串对象,则只在堆中创建该字符串对象。
String str="aaa";
如上代码创建了几个对象呢?答案是1个。这里涉及到字符串常量池,在JVM中有一个字符串池,它用来保存很多可以被共享的String对象,这样如果我们在使用同样字面字符串时,它就使用字符串池中同字面的字符串。当上面代码被执行的时候,JVM会先在常量池中查找是否已存在"aaa"的字符串对象。若是存在,则直接返回该对象的引用。若是不存在,则创建该字符串的对象,加入到字符串常量中。
String str="aaa"+"bbb";
如上代码创建几个对象呢?答案是1个。
JVM编译可以对字符串常量直接相加的表达式进行优化,不必要等到运行期去进行加法运算处理,而是在编译时去掉其中的加号,直接将其编译成一个这些常量相连的结果。如下代码即可验证上面观点。
String s = "aaa" + "bbb";
System.out.println(s == "aaabbb");//true
String str1 = "aaa";
String str2 = "bbb";
String str3 = "aaabbb";
str4 = str1 + "bbb";//会产生新的字符串对象
System.out.println(str3 == str4);//false
如上代码str3==str4结果是flase。是因为str4=str1+"bbb";中str1是变量而不是常量。如果“+”连接的两字符串中只要有一个不是字面常量串(即定义过的),是会产生新的字符串对象。
但是依旧有特殊情况,即被final修饰过后的字符串变量(定义时即被初始化)也可以的当作常量字符串。如下即可证明,final修饰过的已被初始化的变量,在JVM编译时即当作常量来被编译。
final String str1 = "aaa";
final String str2 = "bbb";
String str3 = "aaabbb";
/*
* 因为str1与str2都定义成了常量,所以编译时就能确定,编译时就会将常量替换,等同于
* str4 = "aaa"+"bbb",因此不产生新对象
*/
String str4 = str1 + str2;
System.out.println(str3 == str4);//true
但final修饰的变量未初始化,在代码块中进行修饰化的话,则无法被当作常量来进行编译。因为JVM进行编译时变量未被初始化,在代码块中被初始化,无法确定值,只有运行中才能确定值。如下所示:
//此时str1与str2相当于变量,而不是常,因为块是在运行时才能确定,在编译时不能确定
final static String str1;
final static String str2;
static {
str1 ="aaa";
str2 ="bbb";
}
public static void main(String[] args){
String str3 = str1 + str2;
String str4 ="aaabbb";
System.out.println(str3==str4); //输出为false
}
String str=" ";与String str=new String();
str=" "会放入池中,但new String()不会放入池中。
String的intern()方法
该方法时获取String字符串在常量池中的对象字符串。它遵循对于任何两个字符串 s 和 t,当且仅当 s.equals(t) 为 true 时,即两个变量的值想等时,s.intern() == t.intern() 才为 true”。
总结
创建字符串对象放入常量池中有两种方式:第一种是直接使用字符串常量定义时,如String str = "aaa",str会在常量池中创建字符串对象。第二种是使用带有常量形式字符串参数的字符串构造函数,也就是说只能是String str = new String("aaa")的形式,而不能是先定义 String s = "aaa",然后再使用 String str=new String(s);来创建对象,new String(s);此时只创建一个对象。
其实,当我们仔细研究时,发现放入池中只实质上只存在一种时机,那就是:直接使用字面常量字符串时。上面所说的两种时机实质上就是直接使用了字面常的字符串而将其放入池中的。