先看下这个:
8种基本类型的包装类和常量池
java中基本类型的包装类的大部分都实现了常量池技术,
即Byte,Short,Integer,Long,Character,Boolean;而两种浮点数类型的包装类Float,Double并没有实现常量池技术。另外String类也涉及到了常量池技术;
可参考:
Java常量池理解与总结
8种基本类型的包装类和常量池简单介绍
下面开始:
String 是字符串常量(线程安全);
StringBuffer(线程安全), StringBuilder(非线程安全) 是字符串变量。
String、StringBuilder、StringBuffer类定义
它们三个类都被final说明这三个都是不可被继承的类。
StringBuilder、StringBuffer这两个类都继承了AbstractStringBuilder。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
String、StringBuilder、StringBuffer创建
String类中包含一个不可变的char数组用来存放字符串
/** 这个value被用于存放char变量 */
private final char value[];
public String() {
this.value = "".value;
}
StringBuilder和SpringBuffer都使用父类的构造函数进行初始化
public StringBuilder() {
super(16);
}
public StringBuffer() {
super(16);
}
//这里super(16)调用了父类的有参构造函数:
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
//char[] value = new char[capacity];创建了一个没有字符长度为16的字符型数组value
父类的构造方法,
可以看到和String类的区别是value类没有使用final类去修饰,所以在高并发下对value的操作是不安全的。
char[] value;
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
String、StringBuilder、StringBuffer的操作
String的添加操作只需要使用+号连接就行
StringBuilder、StringBuffer则需要使用append方法进行操作。
但是String使用+号操作,在不满足常量优化机制时,jvm进行编译过后也是使用StringBuilder去进行连接操作的。
StringBuilder、StringBuffer他们两的append方法区别就在于StringBuilder没有同步锁,而StringBuffer加了同步锁。
他们同时都是调用父类的方法进行实现。
父类的append类似于Arraylist的add,在超出一定容量之后都是要对value数组进行扩容
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
总结
大部分情况下效率StringBuilder > StringBuffer > String
StringBuilder 和 StringBuffer在创建字符串及对其进行操作的优势在于都是使用的统一个对象,而String的value是不可变的数组,所以在进行String 的相关操作的时候会产生许多临时的 String对象,再把这些对象引用过去,效率会很低下。
不过,一般做为简单的字符串传递和其它操作,只不要改变字符串内容的操作,用 String 效率会高一些
public class Demo_StringBuffer {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer();
StringBuffer sb2 = sb.append(true);
//System.out.println(sb2.toString()) //这里输出结果为true
StringBuffer sb3 = sb.append("java");
StringBuffer sb4 = sb.append(100);
//这一步结束之后sb,sb1,sb2,sb3这四个引用指向同一个对象,输出为truejava100
System.out.println(sb.toString());
//StringBuffer类中重写了toString方法,显示的是对象中的属性值,sb.toString()后面的.toString()可以省略
System.out.println(sb2);
System.out.println(sb3);
System.out.println(sb4);
}
}
输出结果:
truejava100
truejava100
truejava100
truejava100
因为输出语句都在sb4之后,所以不会像那样一层一层地返回出来,而是全部输出都一样.这也间接地说明StringBuffer在声明和操作时只会占用一个空间节约内存空间,StringBuilder也是一样.
StringBuffer是字符串缓冲区,当new的时候是在堆内存创建了一个对象,底层是一个长度为16的字符数组.当调用添加的方法时,不会再重新创建对象,在不断向原缓冲区添加字符
StringBuffer中的常见方法
注意StringBuffer类中的一个方法:
public StringBuffer insert(int offset,String str):
*这里是在指定位置把任意类型的数据插入到字符串缓冲区里面,并返回字符串缓冲区本身
*注意这里是插入在这个索引的位置,而不是插入在这个对象原来索引位置上的字符后面
StringBuffer的其他方法
StringBuffer的替换功能
public StringBuffer replace(int start,int end,String str):
*从start开始到end用str替换,包含头,不包含尾
StringBuffer的删除功能
public StringBuffer delete(int start,int end):
*删除从指定位置开始指定位置结束的内容,并返回.本身包含头,不包含尾
可以:
StringBuffer sb = new StringBuffer();
sb.append("ilovejava");
sb.delete(0, sb.length() //这样清空缓冲区
StringBuffer的截取功能
public String substring(int start,int end):
*截取从指定位置开始到结束位置,包括开始位置,不包括结束位置
这个方法源码如下:
@Override
public synchronized String substring(int start, int end) {
return super.substring(start, end);
}
所以注意截取方法的返回值类型不再是StringBuffer本身,而是String
String和StringBuffer作为参数传递的问题
首先,基本数据类型的值传递,不改变其值;而引用数据类型的值传递,改变其值
但是String类虽然是引用数据类型,但是他当作参数传递时和基本数据类型是一样的,代码如下:
public class Test {
public static void main(String[] args) {
String s = "Java";
System.out.println(s);
change(s);
System.out.println(s);
System.out.println("---------------------");
StringBuffer sb = new StringBuffer();
sb.append("Java"); //在调用append方法时,会产生一个地址值
System.out.println(sb);
change(sb); //两个引用指向同一个对象(两个地址值给同一个对象)
System.out.println(sb);
}
public static void change(StringBuffer sb) {
sb.append("Script");
}
public static void change(String s) {
s += "Script";
}
}
输出结果:
Java
Java
---------------------
Java
JavaScript
这也说明了String对象是不可变的
另外关于java基本数据类型传递与引用传递区别可以参考:
java基本数据类型传递与引用传递区别详解