1.String内存解析:
字符串不属于基本数据类型,但是可以通过字面量赋值”=”,也可以使用new关键字。
使用“=”与使用new关键字有很大的区别。
下面先介绍几个存储区域:
栈:存放引用(基本数据类型与对象的引用),是指向对象实际存储位置的指针(堆空间或者常量池)。
堆:对象的实际存储位置(使用关键字new创建),栈空间内的引用保存的是对应堆空间的内存地址。
常量池:存放显式的String常量和基本数据类型,常量池内的数据可以共享
public void testString(){
String str1 = "abc";
String str4 = "abc";
String str2 = "def";
String str3 = "abcdef";
String str11 = str1 + "def";
//从反编译中可得str5指向的是str5在堆空间中的内存地址(使用StringBuilder连接)
String str5 = str1 + str2;
String str6 = str5.intern();
//str7的引用直接指向常量池中"abcdef"的内存地址
String str7 = "abc" + "def";
//结果为true,因为str1与str4("="赋值)指向的是常量池的内存地址,而常量池内的数据可以共享。当创建str1时因为常量池内没有"abc",创建str2是常量池内已存在"abc",所以不再重新创建
System.out.println(str1 == str4);//true
//str5使用操作符重载创建,并且"="右侧包含变量,其本质是使用StringBuilder(new 关键字)创建对象(可以使用javap命令查看反编译结果),所有str5指向的是堆空间的引用,而堆空间指向的是常量池
System.out.println(str3 == str5);//false
//intern()方法,查看api可以发现其返回字符串的规范化表达形式,当其被调用后,如果常量池中存在一个相同的字符串,那么直接返回这个字符串,否则将其添加到常量池中去,所以虽然str5指向堆空间,但是str6指向的是常量池
System.out.println(str3 == str6);//true
//str7虽然使用操作符重载,但是"="右侧没有变量
System.out.println(str3 == str7);//true
//与str5原理相同
System.out.println(str11 == str3);//false
System.out.println("str3's hasCode is:" + Integer.toHexString(str3.hashCode()));
System.out.println("str5's hasCode is:" + Integer.toHexString(str5.hashCode()));
System.out.println("str7's hasCode is:" + Integer.toHexString(str7.hashCode()));
//final常量在编译后会直接替换为对应的值,在这种情况下编译器会直接将str8与str9合并
final String str8 = "a";
final String str9 = "bc";
String str10 = str8+str9;
//final常量在编译后会直接替换为对应的值,在这种情况下编译器会直接将str8与str9合并
//编译后为String Str10 = "a" + "bc";
System.out.println(str10 == str1);//true
}
2.String与StringBuilder的区别
查看源码可发现String是final类,而StringBuilder不是final类
/**
* 值传递与引用传递
*/
@Test
public void testStringBuilder(){
StringBuilder str1 = new StringBuilder("a");
StringBuilder str2 = new StringBuilder("b");
String str3 = "a";
String str4 = "b";
append(str3, str4);
append(str1, str2);
System.out.println(str1 + " " + str2);//ab b
System.out.println(str3 + " " + str4);//a b
}
/**
*
* @param a
* @param b
* 调用此方法后,a的指针与str1相同,b与str2的指针相同
* StringBuilder是可变字符序列
*/
public void append(StringBuilder a, StringBuilder b){
//a.append(b),a的指针并没有改变,与str1相同
a.append(b);
//b的指针与str2相同,但是执行b = a操作后,b的指针指向了a在堆空间的内存地址,但是不会改变str2的指针
b = a;
}
/**
*
* @param a
* @param b
* 调用此方法后,a的指针与str1相同,b与str2的指针相同
* String为final类,不可变
*/
public void append(String a,String b){
//a与str1指针相同,但是String是final类,执行a=a+b后,a的指针会改变,str1的指针不会改变
a = a + b;
//执行b=a后,b的指针会改变,但是str2的指针并不会改变
b = a;
}
结合String内存解析,会深刻理解两者的区别