一、Java中字符串常量和字符串变量的区别
https://blog.csdn.net/u010617952/article/details/51781319
字符串常量池实现的前提条件:Java中String对象是不可变的,这样可以安全保证多个变量共享同一个对象。如果Java中的String对象可变的话,一个引用操作改变了对象的值,那么其他的变量也会受到影响,显然这样是不合理的。
String s1 = "s"; //不可变对象,字符串常量,先在字符串常量池里搜索,没有,则new,再加入,成拘留字符串;有就直接引用。
String s2 = "s";
System.out.println("s1 == s2:" + (s1 == s2)); //true,注意添加括号,字符串拼接优先于判定
String s3 = new String("s"); //字符串变量,不管常量池中有没有,都会创建新对象
System.out.println("s1 == s3:" + (s1 == s3)); //false
String s4 = s3.intern(); //intern()将其加入字符串常量池
System.out.println("s4 == s3:" + (s4 == s3)); //false
System.out.println("s1 == s4:" + (s1 == s4)); //true
二、Java中字符串拼接的一些细节分析
https://www.jb51.net/article/59934.htm
- 内部编译器优化,连续用+拼接,只创建一个对象。
String s = "1" + "2" + "3";
- 如果是for循环用+拼接,会隐式创建StringBuilder,有多少个循环创建多少个。因此,要避免在for循环内隐式或显式创建StringBuilder,提升性能。
- 在for循环外部显式创建StringBuilder,使用append方法,再toString即可。
int[] arr = new int[] {1,2,3};
StringBuilder sb = new StringBuilder();
for(int i : arr) {sb.append(i);}
System.out.println(sb.toString());
三、String、StringBuffer与StringBuilder之间区别
https://blog.csdn.net/itchuxuezhe_yang/article/details/89966303
1. String类型脱光了其实也很普通。真正让她神秘的原因就在于CONSTANT_String_info常量表和拘留字符串对象的存在。
【纷争一】关于字符串相等关系的争论
上面第一部分已写明。
【纷争二】字符串“+”操作的内幕
//代码1
String sa = "ab";
String sb = "cd";
String sab=sa+sb;
String s="abcd";
System.out.println(sab==s); //false
//代码2
String sc="ab"+"cd";
String sd="abcd";
System.out.println(sc==sd); //true
代码1中局部变量sa,sb存储的是堆中两个拘留字符串对象的地址。而当执行sa+sb时,JVM首先会在堆中创建一个StringBuilder类,同时用sa指向的拘留字符串对象完成初始化,然后调用append方法完成对sb所指向的拘留字符串的合并操作,接着调用StringBuilder的toString()方法在堆中创建一个String对象,最后将刚生成的String对象的堆地址存放在局部变量sab中。而局部变量s存储的是常量池中"abcd"所对应的拘留字符串对象的地址。 sab与s地址当然不一样了。这里要注意了,代码1的堆中实际上有五个字符串对象:三个拘留字符串对象、一个String对象和一个StringBuilder对象。
代码2中"ab"+"cd"会直接在编译期就合并成常量"abcd", 因此相同字面值常量"abcd"所对应的是同一个拘留字符串对象,自然地址也就相同。
总结:字符串引用相“+”,会创建StringBuilder,append合并,再new一个新对象作为结果。拘留字符串相“+”,在编译器就合并,成为拘留字符串。
2. String三姐妹(String,StringBuffer,StringBuilder)
- StringBuffer与String的可变性问题
//String
public final class String
{
private final char value[]; //final,不可变
public String(String original) {
// 把原字符串original切分成字符数组并赋给value[];
}
}
//StringBuffer
public final class StringBuffer extends AbstractStringBuilder
{
char value[]; //继承了父类AbstractStringBuilder中的value[] ,可变
public StringBuffer(String str) {
super(str.length() + 16); //继承父类的构造器,并创建一个大小为str.length()+16的value[]数组
append(str); //将str切分成字符序列并加入到value[]中
}
}
总结:本质上是指对象中的value[]字符数组可不可变,而不是对象引用可不可变。
- String和StringBuffer的效率问题
(1) String常量与String变量的"+"操作比较
String str = "";
str="Heart" + "Raid";
String s1 = "Heart";
String s2 = "Raid";
String str = "";
str = s1 + s1;
结论:String常量的“+连接” 稍优于 String变量的“+连接”。
原因:测试①的"Heart"+"Raid"在编译阶段就已经连接起来,形成了一个字符串常量"HeartRaid",并指向堆中的拘留字符串对象。运行时只需要将"HeartRaid"指向的拘留字符串对象地址取出1W次,存放在局部变量str中。这确实不需要什么时间。
(2) String对象的"累+"连接操作与StringBuffer对象的append()累和连接操作比较
String s1="Heart";
String s="";
s=s+s1;
String s1="Heart";
StringBuffer sb=new StringBuffer();
sb.append(s1);
结论:大量字符串累加时,StringBuffer的append()效率远好于String对象的"累+"连接
原因:测试① 中的s=s+s1,JVM会利用首先创建一个StringBuilder,并利用append方法完成s和s1所指向的字符串对象值的合并操作,接着调用StringBuilder的 toString()方法在堆中创建一个新的String对象,其值为刚才字符串的合并结果(就是字符串变量相+)。而局部变量s指向了新创建的String对象。
总结:
(1) 在编译阶段就能够确定的字符串常量,完全没有必要创建String或StringBuffer对象。直接使用字符串常量的"+"连接操作效率最高。
(2) StringBuffer对象的append效率要高于String对象的"+"连接操作。
(3) 不停的创建对象是程序低效的一个重要原因。那么相同的字符串值能否在堆中只创建一个String对象。显然拘留字符串能够做到这一点,除了程序中的字符串常量会被JVM自动创建拘留字符串之外,调用String的intern()方法也能做到这一点。当调用intern()时,如果常量池中已经有了当前String的值,那么返回这个常量指向拘留对象的地址。如果没有,则将String值加入常量池中,并创建一个新的拘留字符串对象。