String
String对象不可变,当对象创建完毕之后,如果内容改变则会创建一个新的String对象,返回到原地址中。
不可变优点:
多线程安全。
节省空间,提高效率。
源码:
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
private final byte[] value;
private final byte coder;
private int hash; // Default to 0
public String(String original) {
this.value = original.value;
this.coder = original.coder;
this.hash = original.hash;
}
//new String()都是在堆上创建字符串对象。当调用 intern() 方法时,编译器会将字符串添加到字符串常量池中,并返回指向该常量的引用。
public native String intern();
}
第一种:直接赋一个字面量
String str1 = "ABCD";
最多创建一个String对象(先在字符串常量池先创建"ABCD"内存空间,再引用)
最少不创建String对象( 如果字符串常量池中存在"ABCD",那么str1直接引用。)
第二种:通过构造器创建
String str2 = new String("ABCD");
new 创建字符串时,首先查看池中是否有相同的字符串,如果有则拷贝一份放到堆中,然后返回堆中的地址。
如果池中没有则在堆中创建一分,然后返回堆中的地址。
常量字符串和变量拼接时(String str = baseStr + "01";),会调用StringBuilder.append()在堆上创建新的对象:
String s1 = "abc";
String s2 = "a";
String s3 = "bc";
String s4 = s2 + s3; //
System.out.println(s1 == s4);
A:false,因为s2+s3实际上是使用StringBuilder.append()来完成,会生成不同的对象。
JVM确实对形如 String str = "java"; 的对象放在常量池中,但是它是在编译时做的。
而 String s = str1 + str2; 是在运行时候才能知道的,也就是说 str1 + str2 是在堆里创建的。
String s1 = "abc";
final String s2 = "a";
final String s3 = "bc";
String s4 = s2 + s3;
System.out.println(s1 == s4);
A:true,因为final变量在编译后会直接替换成对应的值,所以实际上等于s4="a"+"bc",而这种情况下,编译器会直接合并为s4="abc",所以最终s1==s4。
StringBuffer(可变字符串)
StringBuffer是为了提高java中字符串连接的效率:
JVM内部采用了StringBuffer来连接字符串(即"+"操作),但是每次有执行"+"操作的语句时,
JVM都要new一个StringBuffer对象来处理字符串的连接,这在涉及很多的字符串连接操作时开销会很大。
所有我们自己使用StringBuffer来处理字符串的连接,可以提高java中字符串连接的效率
底层原理:
AbstractStringBuilder中采用一个char数组来保存需要append的字符串,
char数组有一个初始大小,当append的字符串长度超过当前char数组容量时,则对char数组进行动态扩展,
也即重新申请一段更大的内存空间,然后将当前char数组拷贝到新的位置,
因为重新分配内存并拷贝的开销比较大,所以每次重新申请内存空间都是采用申请大于当前需要的内存空间的方式,这里是2倍
StringBuilder
StringBuffer 与 StringBuilder 中的方法和功能完全是等价的。
Java SE5引入的,线程安全(该对象方法中所有的方法都是用了synchronized修饰符),因此开销也会大。
常量池详解
https://www.cnblogs.com/loveer/p/11519739.html