String类、StringBuffer类、StringBuilder类三者之间的关系
- String类对字符串的保存格式:
private final char value[];
从源码中可以看出来,它是被final
修饰的,是不可变的,从它被创建直至被销毁,它的字符序列都没有改变,我们对它的一系列操作都是通过创建新的String对象来完成的。
- StringBuilder、StringBuffer两个类对字符串的保存格式:
char[] value;
StringBuilder、StringBuffer两个类都继承自抽象类AbstractStringBuilder,它们是可变的。
- StringBuilder、StringBuffer与String类之间的转换
public abstract String toString();
当我们通过StringBuilder、StringBuffer对象进行一系列操作后得到了我们最终想要的字符串之后,可以通过toString()方法来将StringBuilder、StringBuffer对象转化为String对象。
StringBuilder a = new StringBuilder("hello");
同时也可以将String类型转换为StringBuilder或者StringBuffer类型。
线程安全相关的问题
- String类
String中的对象是不可变的,可以理解为常量,显然线程安全。
- StringBuffer类与StringBuilder类
public synchronized StringBuffer append(Object obj) {
super.append(String.valueOf(obj));
return this;
}
public synchronized StringBuffer reverse() {
super.reverse();
return this;
}
从代码可以看出来StringBuffer类是被synchronized修饰的,所以是线程安全的,StringBuffer和StringBuilder类功能基本相似,最大的区别在与StringBuilder是没有被synchronized修饰的,因此线程不安全,但是StringBuilder类的速度要比String类与StringBuffer要快一点。
StringBuffer是线程安全的,这意味着它们已经同步方法来控制访问,以便只有一个线程可以在同一时间访问一个StringBuffer对象同步代码。因此,StringBuffer的对象通常在多线程环境中是安全的,使用多个线程可以试图同时访问相同StringBuffer对象。
StringBuilder类不是线程安全的。由于不同步,StringBuilder的性能可以比StringBuffer更好。因此,如果在单线程环境中工作,使用StringBuilder,而不是StringBuffer可能会有更高的性能。
- 总结
String 字符串常量
StringBuffer 字符串变量(线程安全)
StringBuilder 字符串变量(非线程安全)
在大部分情况下速度: StringBuilder > StringBuffer
在大部分情况下速度: StringBuffer > String
速度测试代码
public class Test {
final static int time = 50000; //循环次数
public static void main(String[] args) {
Test test = new Test();
String s1 = "String类测试:";
StringBuffer st1 = new StringBuffer( "StringBuffer类测试:");
StringBuilder st2 = new StringBuilder( "StringBuilder类测试:");
test.test(s1);
test.test(st1);
test.test(st2);
test.test2();
test.test3();
}
/*
* String类测试方法
*/
public void test(String s){
long begin = System.currentTimeMillis();//获取当前系统时间(毫秒数),开始
for(int i=0; i<time; i++){
s += "hello";
}
long over = System.currentTimeMillis();//获取当前系统时间(毫秒数),结束
System.out.println("操作"+s.getClass().getName()+"类型使用的时间为:"+(over-begin)+"毫秒");
}
/*
* StringBuffer类测试方法
*/
public void test(StringBuffer s){
long begin = System.currentTimeMillis();
for(int i=0; i<time; i++){
s.append("hello");
}
long over = System.currentTimeMillis();
System.out.println("操作"+s.getClass().getCanonicalName()+"类型使用的时间为:"+(over-begin)+"毫秒");
}
/*
* StringBuilder类测试方法
*/
public void test(StringBuilder s){
long begin = System.currentTimeMillis();
for(int i=0; i<time; i++){
s.append("hello");
}
long over = System.currentTimeMillis();
System.out.println("操作"+s.getClass().getName()+"类型使用的时间为:"+(over-begin)+"毫秒");
}
/*对 String 直接进行字符串拼接的测试*/
public void test2(){//操作字符串对象引用相加类型使用的时间
String s2 = "abcd";
long begin = System.currentTimeMillis();
for(int i=0; i<time; i++){
String s = s2 + s2 +s2;
}
long over = System.currentTimeMillis();
System.out.println("操作字符串对象引用相加类型使用的时间为:"+(over-begin)+"毫秒");
}
public void test3(){//操作字符串相加使用的时间
long begin = System.currentTimeMillis();
for(int i=0; i<time; i++){
String s = "abcd" + "abcd" + "abcd";
}
long over = System.currentTimeMillis();
System.out.println("操作字符串相加使用的时间为:"+(over-begin)+"毫秒");
}
}
//测试结果:
操作java.lang.String类型使用的时间为:9954毫秒
操作java.lang.StringBuffer类型使用的时间为:10毫秒
操作java.lang.StringBuilder类型使用的时间为:0毫秒
操作字符串对象引用相加类型使用的时间为:15毫秒
操作字符串相加使用的时间为:0毫秒
结果可以看出,在不必考虑到线程同步问题,我们应该优先使用StringBuilder类;如果要保证线程安全,自然是StringBuffer;能直接操作字符串不用字符串引用就直接操作字符串
StringBuilder类与StringBuffer类的相同点
方法 | 说明 |
---|---|
StringBuffer append(参数) | 追加内容到当前StringBuffer对象的末尾,类似于字符串的连接 |
StringBuffer deleteCharAt(int index) | 删除指定位置的字符,然后将剩余的内容形成新的字符串 |
StringBuffer insert(位置, 参数) | 在StringBuffer对象中插入内容,然后形成新的字符串 |
StringBuffer reverse() | 将StringBuffer对象中的内容反转,然后形成新的字符串 |
void setCharAt(int index, char ch) | 修改对象中索引值为index位置的字符为新的字符ch |
void trimToSize() | 将StringBuffer对象的中存储空间缩小到和字符串长度一样的长度,减少空间的浪费,和String的trim()是一样的作用 |
StringBuffer delete(int start, int end) | 删除指定区域的字符串 |
StringBuffer replace(int start, int end, String s) | 用新的字符串替换指定区域的字符串 |
void setlength(int n) | 设置字符串缓冲区大小 |
int capacity() | 获取字符串的容量 |
void ensureCapacity(int n) | 确保容量至少等于指定的最小值。如果当前容量小于该参数,然后分配一个新的内部数组容量更大。新的容量是较大的 |
getChars(int start,int end,char chars[],int charStart); | 将字符串的子字符串复制给数组 |
StringBuilder类与StringBuffer类常用方法测试代码
public class Test {
public static void main(String[] args) {
Test test = new Test();
StringBuffer strbf = new StringBuffer("Hello Swen");
StringBuilder strbl = new StringBuilder(30);
System.out.println("testStringBuffer如下:");
test.testStringBuffer(strbf);
System.out.println("testStringBuilder如下:");
test.testStringBuilder(strbl);
}
/*
* StringBuffer类测试方法
*/
public void testStringBuffer(StringBuffer str){
//增加字符串内容的方法
//append(参数),追加内容到当前对象的末尾
str.append(" 你要加油学习!");
System.out.println("追加内容到当前对象的末尾:"+str);
// insert(位置, 参数),在对象中插入内容
str.insert(10,',');
System.out.println("在对象中插入内容:"+str);
//操作字符串内容的方法
//delete(int start, int end),删除指定区域的字符串
str.delete(10, 18);
System.out.println("删除指定区域的字符串:"+str);
//deleteCharAt(int index),删除指定位置的字符
str.deleteCharAt(10);
System.out.println("删除指定位置的字符:"+str);
//setCharAt(int index, char newChar),修改对象中索引值为index位置的字符为新的字符ch
str.setCharAt(5, '-');
System.out.println("修改对象中索引值为index位置的字符为新的字符ch:"+str);
//replace(int start, int end, String s), 用新的字符串替换指定区域的字符串
str.replace(6, 10, "world");
System.out.println("用新的字符串替换指定区域的字符串:"+str);
// reverse()内容反转
str.reverse();
System.out.println("内容反转:"+str);
//将字符串的子字符串复制给数组。
char[] ch = new char[5];
str.getChars(0, 3, ch, 0);
System.out.println("将字符串的子字符串复制给数组:"+Arrays.toString(ch));
}
/*
* StringBuilder类测试方法
*/
public void testStringBuilder(StringBuilder str){
str.append("Hello Swen");
System.out.println("追加内容到当前对象的末尾:"+str);
//length(),获取字符串长度
System.out.println("字符串长度为:"+str.length());
//capacity(),获取字符串的容量
System.out.println("字符串容量为:"+str.capacity());
//有关字符串空间的方法
//setLength(int newSize),设置字符串缓冲区大小
str.setLength(20);
System.out.println("setLength后,字符串长度为:"+str.length());
System.out.println("setLength后,字符串容量为:"+str.capacity());
//trimToSize(),存储空间缩小到和字符串长度一样的长度
str.trimToSize();
System.out.println("trimToSize后,字符串长度为:"+str.length());
System.out.println("trimToSize后,字符串容量为:"+str.capacity());
}
}
StringBuilder类与StringBuffer类常用方法测试结果
testStringBuffer如下:
追加内容到当前对象的末尾:Hello Swen 你要加油学习!
在对象中插入内容:Hello Swen, 你要加油学习!
删除指定区域的字符串:Hello Swen!
删除指定位置的字符:Hello Swen
修改对象中索引值为index位置的字符为新的字符ch:Hello-Swen
用新的字符串替换指定区域的字符串:Hello-world
内容反转:dlrow-olleH
将字符串的子字符串复制给数组:[d, l, r, , ]
testStringBuilder如下:
追加内容到当前对象的末尾:Hello Swen
字符串长度为:10
字符串容量为:30
setLength后,字符串长度为:20
setLength后,字符串容量为:30
trimToSize后,字符串长度为:20
trimToSize后,字符串容量为:20
网友评论