String、StringBuilder和StringBuffer的区别
来源:互联网 发布:小程序第三方平台源码 编辑:程序博客网 时间:2024/06/03 20:39
String、StringBuilder和StringBuffer三个类都是用于操作字符串的java.lang包下的工具类。在字符串连接操作比较频繁情况下,StringBuilder、StringBuffer代替“+”操作符能带来更好的性能。另外,StringBuffer在多线程环境下比StringBuilder更安全,因为它给大部分方法加上了synchronized锁关键字,而在单线程环境下StringBuilder比StringBuffer性能更好,因为线程不必等待获取对象锁。此外,在结构上具有的共性、差异如图所示。
String
String类是编程中使用最频繁的工具类,以至于JVM将String类的实例创建和操作都进行了特殊处理,如”aaa”直接创建一个String实例,而不用通过new String构造函数的方式进行,此外还可以通过”+”连接符直接将两个字符串进行拼接,同时创建第三个字符串实例,同时进行了拼接和创建两个操作,如下面的示例代码所示。
public static void main(String[] args) { final String str1 = new String("Hello"); final String str2 = String.valueOf("World"); System.out.println(str1 + "," + str2);}
三个字符串实例str1、str2和”,”通过内置的“+”连接(每一次连接都会创建一个全新的对象),最终在创建一个全新的字符串实例“Hello, World”。在上面的字符串实例创建过程有三种方式:new、String.valueOf工厂方法和字符串字面量等。字面量是我们平常编程使用最广泛的方式,但也只是在字符内容确定前提下,而类似于toString的方法创建字符串又很容易忽视NullPointerException检查,利用String.valueOf静态工厂方法来创建则可以获取到一个字符串,但对于Null值获取到的是一个”null”,这有时候会造成一定的程序问题,例如字符转成数值类型。
- 内部实现
String作为java.lang包中的工具类,它所操作的是字符char,而char是作为基本类型在JVM中存在,也就是char是一个基本单元,同时Character才是char所对应的包装类型。String操作的是char数组,这点在String的内部封装中可以看见,并且使用final修饰,也就是在一开始赋值以后便不能再变动。
public final String implements Serializable, Comparable<String>, CharSequence { private final char value[]; private int hash; private static final long seriaVersionUID = -68497944..; private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0]; public String() { this.value = new char[0]; } public void getChars(int paramInt1, int paramInt2, char[] paramArrayOfChar, int paramInt3) { if (paramInt1 < 0) { throw new StringIndexOutOfBoundsException(paramInt1); } if (paramInt2 > this.count) { throw new StringIndexOutOfBoundsException(paramInt2); } if (paramInt1 > paramInt2) { throw new StringIndexOutOfBoundsException( paramInt2 - paramInt1); } //内部操作时通过System.arraycopy的底层native方法实现 System.arraycopy(this.value, this.offset + paramInt1, paramArrayOfChar, paramInt3, paramInt2 - paramInt1); } }... ... ...
数组是一个类型一致的数据的聚合对象,而该对象是有序的、个数已知的、不可改变的对象,所以String对象是一个值不可变的对象。String的连接操作时合并数组,重现创建一个更长数组,以及String对象的过程,所以如果“+”太频繁时,会带来性能消耗。
- 内存分析
一个String对象的大小由value字段决定,它的长短就是一个char[]的大小。上面的字符连接操作在内存中的过程如下图所示。这其中$1、$2
是一个匿名的内部指针命名,当然是我为了说明方便而加。
StringBuilder
在23中设计模式中,建造者Builder模式是创建对象的一种模式,它可以将复杂的对象创建过程隐藏,而只暴露给客户端一个调用接口。那么StringBuilder是否就是String的建造模式的具体实现?StringBuilder是采用char[16]数组作为默认存储容器,在append操作时会动态扩展内部数组,从而实现字符串的拼接,如下面代码所示。
public static void main(String[] args) { final String str1 = "hello"; final String str2 = "world" final StringBuilder strBuilder = new StringBuilder(); strBuilder.append(str1).append(",").append(str2);}
- 内部实现
StringBuilder作为java.lang包中的工具类,它所操作的也是char。StringBuilder内部也是通过char[]数组作为字符串的存储容器,但不同于String工具类,它的char[]是可以动态扩展,也即char[]的长度在字符不断进行append时进行增加。StringBuilder内部实现如下所示。
public class StringBuilder extends AbstractStringBuilder implements Serializable, CharSequence { public StringBuilder() { super(16); } public StringBuilder(int capacity) { super(capacity) } public StringBuilder(CharSequence paramCharSequence) { this(paramCharSequence.length() + 16); append(paramCharSequence); } public StringBuilder append(String paramString) { super.append(paramString); return this; } // ... ... ...}//StringBuilder、StringBuffer的父类abstract class AbstractStringBuilder implements Appendable, CharSequence { char[] value; int count; static final int[] sizeTable = {9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999, 2147483647}; AbstractStringBuilder(int paramInt) { this.value = new char[paramInt]; } void expandCapacity(int paramInt) { int i = (this.value.length + 1) * 2; if (i < 0) i = 2147483647; else if (paramInt > i) { i = paramInt; } char[] arrayOfChar = new char[i]; System.arraycopy(this.value, 0, arrayOfChar, 0, this.count); this.value = arrayOfChar; } public AbstractStringBuilder append(String paramString) { if(paramString == null) paramString = "null"; int i = paramString.length(); if(i == 0) return this; int j = this.count + i; if(j > this.value.length) { expandCapacity(j); } paramString.getChars(0, i, this.value, this.count); this.count = j; return this; } //... ... ... }
在进行append操作时,AbstractStringBuilder的append是StringBuilder的底层实现,其逻辑基础是附加数组前提是判断当前value是否能够容纳paramString,如果value.length长度不够,则调用expandCapacity扩展value,然后再有native方法System.arraycopy将paramString的值填充到value字符数组中,这也是StringBuilder附加字符串的逻辑实现。此外,StringBuilder不同于String工具类的根本原因在于AbstractStringBuilder的value是非终结的字段,而String的value是final修饰的终结字段,所以一个可以扩展而另一个不能扩展。
StringBuffer
StringBuilder可以动态的附加字符到内部存储容器char[]中,但是它的各项操作都是非线程安全的,在多线程环境下,可能会抛出意想不到异常,如下面代码所示。
public static void main(String[] args) { final StringBuilder strBuilder = new StringBuilder(); //final StringBuffer strBuffer = new StringBuffer(); new Thread(new Runnable() { public void run() { for(int i = 1; i <= 10000; i++) { //多线程不安全操作 strBuilder.append(i); System.out.print(strBuilder.charAt(i)+ " "); //线程安全操作 //strBuffer.append(i); //System.out.print(strBuffer.charAt(i)+ " "); } } }).start(); new Thread(new Runnable() { public void run() { for(int i = 1; i <= 10000; i++) { strBuilder.append(i); System.out.print(strBuilder.charAt(i)+ " "); //线程安全操作 //strBuffer.append(i); //System.out.print(strBuffer.charAt(i)+ " "); } } }).start();}
在运行该段代码时,抛出异常的可能性很大,大概的异常信息为下面所示。而造成异常的原因主要是append操作和chaAt操作非原子操作,它们在两个线程里存在潜在的污染(异步性),在第一个线程append时检测value的容量不够,进行扩展,但还未扩展完成时,第二个线程已经检测到改value的容量足够,所以chatAt获取字符时报错。
Exception in thread "Thread-0" java.lang.StringIndexOutOfBoundsException: String index out of range: 1at java.lang.AbstractStringBuilder.charAt( AbstractStringBuilder.java:177)at TestStringBuilder$1.run(TestStringBuilder.java:11)at java.lang.Thread.run(Thread.java:595)
- 内部实现
StringBuffer可以保证在多线程环境下附加字符串不会抛出ArrayIndexOutOfException问题,而它保证的前提是操作的原子性,也即方法加上synchronized关键字,其内部代码如下所示。
public final class StringBuffer extends AbstracStringBuilder implements Serializable,CharSequence { private static final ObjectStreamField[] serialPersisentFields = { new ObjectStreamField("value", [C.class), new ObjectStreamField("count", Integer.TYPE), new ObjectStreamField("shared", Boolean.TYPE) }; public StringBuffer() { super(16); } public StringBuffer(int capacity) { super(capacity); } public synchronized char charAt(int paramInt) { if ((paramInt < 0) || (paramInt >= this.count)) throw new StringIndexOutOfBoundsException(paramInt); return this.value[paramInt]; } public synchronized StringBuffer append(String paramString) { super.append(paramString); return this; } ... ... ...}
结论
String、StringBuilder和StringBuffer都是操作字符的工具类,或者String对象是大小固定的、StringBuilder和StringBuffer对象是大小可扩展的工具类。在字符串使用上String是最常见的工具类,但在字符拼接比较频繁的环境下,StringBuilder具有较高的性能,而在多线程环境下,StringBuffer比StringBuilder安全。
- StringBuffer和StringBuilder和String的区别
- String和StringBuffer和StringBuilder的区别
- StringBuffer和StringBuilder和String的区别
- string和stringBuilder和stringBuffer的区别
- String和StringBuffer和StringBuilder的区别
- String Stringbuffer和StringBuilder的区别
- String Stringbuffer和StringBuilder的区别
- String StringBuffer和StringBuilder的区别?
- String、StringBuffer和StringBuilder的区别
- String 和StringBuffer,StringBuilder的区别
- String,StringBuffer 和StringBuilder的区别
- String、StringBuffer和StringBuilder的区别
- String Stringbuffer和StringBuilder的区别
- String、StringBuffer和StringBuilder的区别
- String 和StringBuffer,StringBuilder的区别
- String、StringBuilder和StringBuffer的区别
- String、StringBuilder和StringBuffer的区别
- String、StringBuilder和StringBuffer的区别
- CCNA Cloud CLDADM 210-455 Official Cert Guide.pdf 英文原版 免费下载
- 软件测试分类划分
- Kafka Producer拦截器
- C#读取pdf文件
- 爱创课堂揭秘好的 web 前端工资有多高?
- String、StringBuilder和StringBuffer的区别
- 2017年11月19日实验感想
- jquery基础
- pandas.qcut与pandas.cut区别
- Struts2框架-1
- MySql 数据库逻辑架构
- Spring 基础知识汇总
- 数据结构之队列
- 编写一个JFrame窗口,要求如下: 1. 在窗口的NORTH区放置一个JPanel面板。 2. JPanel面板放置如下组件: (1) JLable标签,标签文本为“兴趣”,右边接着是三个JChec