String创建对象有两种方式,(2)String str = "abc";(2)String str = new String("abc");下面就对两种方法进行分析。
String str = "abc";
1.第一次String str= "abc",首先在栈中创建一个引用str,然后判断常量池中是否存在“abc”这个常量对象,因为第一次创建,自然没有,所以就创建一个“abc”对象,并且引用str指向这个“abc”对象。
2.第二次String str2 = "abc",由于第一次已经创建了存在于常量池的常量对象“abc”,所以只创建引用str2并指向"abc"。
String str = new String("abc");
3.第一次String str3 = new String("abc"),首先会在栈中创建一个字符串引用str3,然后通过new创建一个“abc”对象,引用str3指向这个堆上的对象,同时又判断常量池中是否有“abc”这个对象,如果没有,则创建,由于前面已经创建,这儿就不会创建。
4.第二次String str4 = new String("abc"),首先会在占中创建一个引用str4,然后new创建一个“abc”对象放在堆中,前面在常量池中创建了“abc”对象,这儿就不会再创建。
小结
对于String str = “abc”;而言,如果每次常量对象都一样,就只有第一次会创建,而对于String str = new String("abc");而言,每次都会创建一个新的对象放在堆中,只是创建池的对象可能不会创建,两种方法每次都会创建引用。
附一
String str = "java";
System.out.println("java" == str.substring(0)); //输出true
System.out.println("ava" == str.substring(1)); //输出false
解释:对于String的subString()方法,如果index==0的话会直接返回当前对象,否则会new一个新的对象返回。
源码:
public String substring(int beginIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
int subLen = value.length - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}
附二
String str2 = "JAVA";
System.out.println(str2 == "java".toUpperCase()); //输出false
System.out.println(str2.equals("java".toUpperCase())); //输出true
解释:String的toUpperCase方法也会创建一个新的对象返回。
源码:
public String toUpperCase(Locale locale) {
//中间操作过长,这里省略
return new String(result, 0, len + resultOffset);
}
附三
String str3 = "java";
String str4 = "ja";
String str5 = str4+"va";
System.out.println(str3 == str5); //输出false
解释:字符串常量和字符串变量相加产生的新字符串需要在堆中创建一个对象来存放。