例子:
String str1 = "a" + "b" + "c"; //内存地址1(常量池:编译时已转换为"abc")
String str2 = "abc"; //内存地址1(常量池)
String str3 = "ab";//(常量池)
final String str4 = "ab"; //(常量池)
String str5 = str3 + "c"; //内存地址3(堆内存:由于编译的时候不能确定str3的值,所以利用StringBuffer拼接字符串)
String str6 = str4 + "c"; //内存地址1(常量池:由于str4用final定义,则不可以修改,因此编译时直接拼接为"abc")
String str7 = new String("abc"); //内存地址4(堆内存)
String str8 = new String(str2); //内存地址5(堆内存)
Log.i(TAG, "str1 == str2 " + (str1 == str2)); //true
Log.i(TAG, "str1.equals(str2) " + str1.equals(str2) + "\n"); //true
Log.i(TAG, "str1 == str5 " + (str1 == str5)); //false
Log.i(TAG, "str1.equals(str5) " + str1.equals(str5) + "\n"); //true
Log.i(TAG, "str1 == str6 " + (str1 == str6)); //true
Log.i(TAG, "str1.equals(str6) " + str1.equals(str6) + "\n"); //true
Log.i(TAG, "str1 == str7 " + (str1 == str7)); //false
Log.i(TAG, "str1.equals(str7) " + str1.equals(str7) + "\n"); //true
Log.i(TAG, "str1 == str8 " + (str1 == str8)); //false
Log.i(TAG, "str1.equals(str8) " + str1.equals(str8) + "\n"); //true
Log.i(TAG, "str7 == str8 " + (str7 == str8)); //false
Log.i(TAG, "str7.equals(str8) " + str7.equals(str8) + "\n"); //true
打印结果:
05-30 17:48:44.051 3057-3057/ str1 == str2 true
05-30 17:48:44.051 3057-3057/ str1.equals(str2) true
05-30 17:48:44.051 3057-3057/ str1 == str5 false
05-30 17:48:44.051 3057-3057/ str1.equals(str5) true
05-30 17:48:44.051 3057-3057/ str1 == str6 true
05-30 17:48:44.051 3057-3057/ str1.equals(str6) true
05-30 17:48:44.051 3057-3057/ str1 == str7 false
05-30 17:48:44.051 3057-3057/ str1.equals(str7) true
05-30 17:48:44.051 3057-3057/ str1 == str8 false
05-30 17:48:44.051 3057-3057/ str1.equals(str8) true
05-30 17:48:44.051 3057-3057/ str7 == str8 false
05-30 17:48:44.051 3057-3057/ str7.equals(str8) true
分析:
个人理解:
常量池:常量存放区域,方便对象重复利用。
栈:定义的属性存放区域
堆:new出来的对象存放的区域
方法区:存放静态变量,静态代码块等
1.str1分析:
String str1 = "a" + "b" + "c";
"a" + "b" + "c"在编译的时候,会被编译器优化为"abc",存在于常量池。
内存关系:栈内存的变量str1的指针指向于常量池的字符串"abc"。
2.str2分析:
String str2 = "abc";
"abc",存在于常量池,str2存在于栈内存
内存关系:栈内存的变量str2的指针指向于常量池的字符串"abc"。
3.str5分析:
String str3 = "ab";
String str5 = str3 + "c";
由于str3是一个变量,所以在编译期间jvm,并不能将直接转换为字符串"abc",而是首先创建一个StringBuilder类,再
用StringBuilder类的append方法将str3和字符串"c"拼接,最后通过StringBuilder的toString()方法返回一个
String赋值给str5,因为字符串"abc"是new出来的所以存在于堆内存。
内存关系:栈内存的变量str5的指针指向于堆内存的String对象,堆内存的String对象指针指向于常量池的字符串对象"abc"。
4.str6分析:
final String str4 = "ab";
String str6 = str4 + "c";
由于变量str4被final修饰,意味着不能修改,因此编译器在编译阶段就会将str4 + "c"简化为字符串"abc",]存在于常
量池。
内存关系:栈内存变量str6的指针指向常量池的字符串"abc"。
5.str7分析:
String str7 = new String("abc");
首先编译器会在常量池当中寻找是否有字符串"abc",假如没有,则会在常量池当中创建字符串"abc",new String("abc")
调用了String类的参数为String的构造方法,这个方法最主要做的事情就是创建一个String对象,并且将"abc"的值浅拷贝
一份给String对象,接着将String对象赋值给str7。
内存关系:栈内存变量str7的指针指向堆内存的String对象,堆内存的String对象指向常量池的"abc"对象。
6.str8分析:
String str2 = "abc"; //内存地址1(常量池)
String str8 = new String(str2);
由于String str2 = "abc";在前面已经确认了字符串"abc"已经在常量池创建了,因此当代码运行到String = str8这句代
码的时候,会创建一个String对象,并且String对象的指针指向于常量池的字符串"abc"。
内存关系:栈内存变量str8的指针指向堆内存的String对象,对内存的String对象指向于常量池的"abc"对象。
由以上分析可知:str1的内存地址 == str2的内存地址 == str6的内存地址。
str1 != str5 != str7 != str8
str5,str7,str8的内存地址各不同。但是str1,str2,str6,str7,str8的值的内存地址一致都是为常量池当中的"abc",
只有str5的值的内存地址在堆内存 。
注:如有不对,请指出,谢谢。