String类与StringBuffer,StringBuilder的区别
今天老师讲到String类,StringBuffer,StringBuilder,容易混淆,做个笔记以便记忆,本人初识java,如有错误或者不足,虚心求各位大神教导
String类
- 包:java.lang.String
java使用String类创建一个字符串变量,字符串变量属于对象。他并不是基本数据类型,是一个类
特点:
-
String类创建对象后不能修改
- 源码中的String类
public final class String implements java.io.Serializable, Comparable<String>, CharSequence
-
通过源码中课发现String类有final修饰,因此它是不允许被继承。这一点主要是从效率和安全方面考虑,效率上面String类型在开发中使用频繁,final类的方法都是final的,编译时可以内联,大大提高了调用效率,安全上,String是Java提供的核心类,这种类是非常底层的,和操作系统交流频繁,如果这种类可以被继承,重写其中的方法,就有可能往才做系统内部写入具有攻击性的代码
-
String的实例对象是不可改变的,任何想要改变字符串的操作,都会产生一个新的实例对象,影响效率
- 代码示例
public static void main(String[] args) { String str="HelloWorld"; long start = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { str+="a"; } long ending = System.currentTimeMillis(); System.out.println("String类运行100000次运行时长"+(ending-start)+"毫秒");
- 运行结果
String类运行100000次运行时长4057毫秒
StringBuffer
- 包: java.lang
我们了解了String一些特点,发现其中的缺陷,String对象一经申明,便不能修改他的内容修改的只是他的地址,频繁的创建新的对象,影响效率。而StringBuffer对象是可以改变它的内容的
-
如果修改StringBuffer内容,修改的是同一个对象
public static void main(String[] args) { StringBuffer sb=new StringBuffer("hello "); System.out.println(System.identityHashCode(sb)+"\t\t"+sb); sb.append("world"); System.out.println(System.identityHashCode(sb)+"\t\t"+sb); }
运行结果
356573597 hello 356573597 hello world
- StringBuffer的内存地址并没有改变。说明修改String内容其实是引用关系的改变。
-
对比String类我们做同样的测试
public static void main(String[] args) { String str=new String("hello "); System.out.println(System.identityHashCode(str)); str=str+"world"; System.out.println(System.identityHashCode(str)); }
运行结果
356573597 1735600054
- String字符串的内存地址改变了。说明修改String内容其实是引用关系的改变。其实是开辟了两个新的字符串堆内存,然后将String对象的内存地址改为"Hello world"字符串的地址,而旧的字符串并没有任何改变,也没有任何引用。这样就产生了两块垃圾空间。这两个垃圾空间会被GC回收
-
StringBuffer不能像String那样直接用字符串赋值,所以也不能那样初始化。它需要通过构造方法来初始化,一共有4个构造方法:
public StringBuffer() public StringBuffer(CharSequence seq) public StringBuffer(int capacity) public StringBuffer(String str)
StringBuffer的常用方法
-
append()
public StringBuffer append(T t) //将指定类型的数据追加到StringBuffer对象的末尾:
-
insert()
public StringBuffer insert(int offset,T t) //将指定类型的数据插入到StringBuffer序列指定位置
-
delete()
public StringBuffer delete(int start,int end) //移除StringBuffer序列中的子字符串,从Start开始,到end-1的字符(注意不是字节,所以一个中文和一个英文都相当于一个字符)
StringBuilder类
- 包:java.lang
StringBuilder是在jdk1.5出现的。StringBuilder拥有和StringBuffer一样的构造方法和方法函数,他们都继承于AbstractStringBuilder,但是它与StringBuffer还是有所不同:
-
线程安全问题:
-
StringBuffer是线程安全的,它所有的public方法都添加了synchronized修饰,而StringBuilder并没有这么做,其中一个源码举例:
//StringBuffer的append()方法 @Override public synchronized StringBuffer append(String str) { toStringCache = null; super.append(str); return this; } //StringBuilder的append()方法 @Override public StringBuilder append(String str) { super.append(str); return this; }
-
-
缓冲区中的区别 :
-
先上源码:
//StringBuffer的toString()方法 @Override public synchronized String toString() { if (toStringCache == null) { toStringCache = Arrays.copyOfRange(value, 0, count); } return new String(toStringCache, true); } //StringBuilder的toString()方法 @Override public String toString() { // Create a copy, don't share the array return new String(value, 0, count); }
-
StringBuffer每次toString都会直接使用toStringCache值来构造一个字符串,而StringBuilder则每次都需要复制一次字节数组,再构造一个字符串。
-
-
性能上面有所区别
-
StringBuffer是线程安全的,所有方法都是同步的,StringBuilder是没有对方法加锁同步的,所以StringBuilder的性能要远大于StringBuffer
-
举例
public static void main(String[] args) { StringBuffer sbf = new StringBuffer("HelloWorld"); long start1 = System.currentTimeMillis(); for (int i = 0; i < 10000000; i++) { sbf.append("a"); } long ending1 = System.currentTimeMillis(); System.out.println("StringBuffer类运行10000000次运行时长"+(ending1-start1)+"毫秒"); long start2 = System.currentTimeMillis(); StringBuilder stb = new StringBuilder("HelloWorld"); for (int i = 0; i < 10000000; i++) { stb.append("a"); } long ending2 = System.currentTimeMillis(); System.out.println("StringBuilder类运行10000000次运行时长"+(ending2-start2)+"毫秒"); }
运行结果
StringBuffer类运行10000000次运行时长524毫秒 StringBuilder类运行10000000次运行时长172毫秒
-
总结:
一般情况下,如果没有字符串改变或者少量改变的场景下,我们可以使用String,多次反复修改可以用StringBuffer或StringBuilder。在使用场景为多线程时使用StringBuffer,如果是单线程则使用StringBuilder。