java进阶-第五讲 StringBuilder 和 StringBuffer
1 StringBuilder与String的区别
String和StringBuilder的底层都是char数组
但是String的char[] 是final修饰的,只能被赋值一次
StringBuilder的char[]数组没有final修饰,是可以被多次赋值的
在StringBuilder中char[] value的赋值,实际上是给StringBuilder构造出来的字符串进行内容的改变。
改变内容是通过数组的扩容,然后指向扩容后的数组来改变数组的内容,从而达到改变字符串内容的效果。
String类型的字符串,一旦定义就不可以改变。
2 StringBuilder的构造方法
// 无参构造
public StringBuilder() {
super(16);
}
无参构造的实现,给我们一个什么信息?
1. StringBuilder的父类是AbstractStringBuilder
2. 它的构造方法直接调用了父类的有参构造
3. 父类的有参构造原型应该是:
public AbstractStringBuilder(int capacity) {
this.value = new char[capacity];
}
4. super(16)告诉我们,无参的StringBuilder()构造出来的StringBuider对象
中,char[] value数组的初始大小是:16
这样就是说,StringBuilder默认的字符串长度是16
// String类型的对象作为参数
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
如果传入的是字符串,那么构造出来的字符串长度应该是传入的 字符串长度+16
补充:
java1.9以后,String和StringBuffer、StringBuilder的底层都由原来的char[]数组变成了byte[]数组。
为什么要这么做?底层有char[] 改成byte[]有什么用?节省了一半的空间。节省空间。
StringBuilder中:
底层的char[] value,问,该数组的作用是什么?
1.char[] value数组在StringBuilder中,就是一个缓存。
2.它可以被多次赋值,但只能指向一个数组对象
3.一旦改变它的指向,原来指向的那个数组对象被GC回收。
4.所以,不论怎么追加字符,改变字符串的内容长度,实际上是这样的:
在原来数组的大小之上建立新数组
将原来数组的内容拷贝到新数组中
将追加的内容放入新数组中
将引用指向新的数组
原来的数组被回收
3 字符串的拼接
在日常开发中,大家往往喜欢对字符串进行拼接。
String str = "12";
String str1 = str + "3";
str1 = str1 + "4";
这样做不好,浪费空间。实际对于开者来说,只是想要具体的结果。
这种做法浪费存储空间。
正确的做法是通过StringBuilder或者是StringBuffer来进行字符串的拼接
因为StringBuilder和StringBuffer底层有一个字符缓存数组。
追加的字符串,不会直接放入字符串常量池中。会先放入堆中的字符数组中。
直到调用toString方法才将最后的结果一次性存入到方法区内存中的字符串常量池中。
因为,只有String类型的才存放到常量池中。
StringBuilder和StringBuffer,不是String类型的。
public class StringBufferTest01 {
public static void main(String[] args) {
StringBuilder stringBuilder = new StringBuilder("abc");
System.out.println(stringBuilder.capacity());
stringBuilder.append("d"); // abcd
stringBuilder.append("e");// abcde
stringBuilder.append("f");// abcdef
stringBuilder.append("g"); // abcdfeg
// "abc" d e f g abcdefg
System.out.println(stringBuilder);
String str = "abc";
String str1 = str + "d";
str1 = str1 + "e";
str1 = str1 + "f";
str1 = str1 + "g";
// abc abcd abcde abcdef abcdefg d e f g
}
}
在以后开发过程中,使用到字符串的时候,推荐使用 String = "xxx"
在进行字符串拼接的时候,尽量少使用"+"去拼接,而是通过StringBuilder或者是StringBuffer
中的append()方法来进行字符串的拼接。
源码追踪图如下:
- 关于StringBuilder中的toString()方法
@Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);// value.length = 16
}
// Create a copy, don't share the array 重新创建了一个String对象。不是共享。
// 这个方法被调用以后,方法区内存中的字符串常量池里才会有一个String常量。
- 关于println()的理解
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("d");
System.out.println(stringBuilder);// Object类型
System.out.println(stringBuilder.toString());// String类型
// 上面两个不是一回事
String string = "a";
System.out.println(string);/// String类型
System.out.println(string.toString());// String类型
// 这两个才是一回事
- String和StringBuilder
他们不是一回事,StringBuilder构造出来的是StringBuilder的对象,这个对象只有toString()之后,才是String类型的字符串。
所以StringBuilder构造出来的对象,不会放在方法区字符串常量池中。只有当他调用toString方法的时候,才会被转换为字符串,字符串最后都会放入到方法区内存的字符串常量池中。
我们不希望,常量池不断地被占用,如果用字符串直接拼接的方式,是不好,这样会造成常量池内存空间的大量占用。那么我们可以使用StringBuilder和StringBuffer来进行字符串的拼接,最后再将他们toString之后,将最后的那一个结果放入到常量池中。这样做节省常量池的空间。
什么时候使用StringBuilder?
/*
String string1 = "中华人民共和国";
String lr = "湖南省长沙市" + string1;
String xr = "新疆自治区" + string1;
address(lr);
*/
StringBuilder st = new StringBuilder("中华人民共和国");
StringBuilder zhangjiajie = st.append("湖南省长沙市");
// "中华人民共和国湖南省长沙市" --- 这个拼接后的结果存在于堆中,它是StringBuilder对象
// 它不是String对象。要将它转成String对象,只有一个方式,调用toString()方法。
// 调用了toString方法以后,实际是在底层通过StringBuilder的char[] value,
// 和数组中的内容创建了一个新的String对象。
String lr = zhangjiajie.toString();
address(lr);
4 StringBuilder 和 StringBuffer的区别
-
StringBuffer是线程安全的
-
StringBuilder不是线程安全的