长久以来,我们被教导字符串的连接最好用StringBuffer、StringBuilder,但是我们却不知道这两者之间的区别.跟字符串相关的一些方法中总是有CharSequence、StringBuffer、StringBuilder、String,他们之间到底有什么联系呢?

1、从类的定义看CharSequence、StringBuffer、StringBuilder、String的关系

下面先贴上这四者的定义(来自JDK1.6)

CharSequence是一个定义字符串操作的接口,StringBuffer、StringBuilder、String中都实现了这个接口.

//CharSequence定义
public interface CharSequence //StringBuffer定义
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence //StringBuilder定义
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence //String定义
public final class String
implements

java.io.Serializable, Comparable<String>, CharSequence

String 是java中的字符串,它继承于CharSequence。
String类所包含的API接口非常多。为了便于今后的使用,我对String的API进行了分类,并都给出的演示程序。

String 和 CharSequence 关系

String 继承于CharSequence,也就是说String也是CharSequence类型。

CharSequence是一个接口,它只包括length(), charAt(int index), subSequence(int start, int end)这几个API接口。除了String实现了CharSequence之外,StringBuffer和StringBuilder也实现了CharSequence接口。

也就是说,CharSequence其实也就是定义了字符串操作的接口,其他具体的实现是由String、StringBuilder、StringBuffer完成的,String、StringBuilder、StringBuffer都可以转化为CharSequence类型。

StringBuilderStringBuffer的区别

StringBuilderStringBuffer都是可变的字符序列。它们都继承于AbstractStringBuilder,实现了CharSequence接口。

但是,StringBuilder是非线程安全的,而StringBuffer是线程安全的。

它们之间的关系图如下:

2、从构造函数到具体的字符串拼接操作看看String、StringBuffer、StringBuilder的区别

下面我们来分析一下String、StringBuffer、StringBuilder具体的构造函数,了解他们是怎么构造出来的,再看看具体的字符串连接操作。

(1)String

String的构造函数(几个常见的构造函数)

public String() {
this.offset = 0;
this.count = 0;
this.value = new char[0];
} /**
* Initializes a newly created {@code String} object so that it represents
* the same sequence of characters as the argument; in other words, the
* newly created string is a copy of the argument string. Unless an
* explicit copy of {@code original} is needed, use of this constructor is
* unnecessary since Strings are immutable.
*
* @param original
* A {@code String}
*/
public String(String original) {
int size = original.count;
char[] originalValue = original.value;
char[] v; if (originalValue.length > size) {
// The array representing the String is bigger than the new
// String itself. Perhaps this constructor is being called
// in order to trim the baggage, so make a copy of the array.
int off = original.offset;
v = Arrays.copyOfRange(originalValue, off, off + size);
} else {
// The array representing the String is the same
// size as the String, so no point in making a copy.
v = originalValue;
} this.offset = 0;
this.count = size;
this.value = v;
} /**
* Allocates a new {@code String} so that it represents the sequence of
* characters currently contained in the character array argument. The
* contents of the character array are copied; subsequent modification of
* the character array does not affect the newly created string.
*
* @param value
* The initial value of the string
*/
public String(char[] value) {
this.offset = 0;
this.count = value.length;
this.value = StringValue.from(value);
}

再看看String中具体的Concat函数

public String concat(String str) {
int otherLen = str.length(); if (otherLen == 0) {
return this;
} char[] buf = new char[count + otherLen];
getChars(0, count, buf, 0);
str.getChars(0, otherLen, buf, count); return new String(0, count + otherLen, buf);
}

从Concat函数中,我们可以知道在对字符串使用concat操作后,具体的操作new出一个等同于两个字符串连接总长度的新的char数组,然后将两个字符串复制到新的char数组中,然后返回一个新的String对象。

(2)StringBuilder

StringBuilder常见构造函数

public StringBuffer() {
super(16);
} public StringBuffer(int capacity) {
super(capacity);
}

从StringBuilder的构造函数中,我们可以看见StringBuilder直接调用父类(AbstractStringBuilder)的构造函数,我们再看看AbstractStringBuilder的构造函数

abstract class AbstractStringBuilder implements Appendable, CharSequence {
final static int[] sizeTable = {
9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999,
Integer.MAX_VALUE
}; /**
* The value is used for character storage.
*/
char[] value; /**
* The count is the number of characters used.
*/
int count; /**
* This no-arg constructor is necessary for serialization of subclasses.
*/
AbstractStringBuilder() {
} /**
* Creates an AbstractStringBuilder of the specified capacity.
*/
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
//其他的一些逻辑
}

从AbstractStringBuilder的构造函数中,我们可以看出StringBuilder中存储字符串其实用的是一个char数组,capacity其实就是指定这个char数组的大小。

下面我们再从StringBuilder中的append函数看看他具体是怎么做的(以 append(String str) 为例看看)。

public StringBuilder append(String str) {
super.append(str);
return this;
}

又是直接调用父类(AbstractStringBuilder)的append方法,再跟到父类中去看看。

/**
* value 用来存储字符串.
*/
char value[]; /**
* 有效字符串的数目.
*/
int count; public AbstractStringBuilder append(String str) {
if (str == null) {
str = "null";
} int len = str.length();
if (len == 0) {
return this;
} int newCount = count + len;
if (newCount > value.length) {
expandCapacity(newCount);
}
//getChars将字符串复制到指定的位置
str.getChars(0, len, value, count);
count = newCount;
return this;
}

上面的逻辑还是比较简单的,在append(str)函数调用的时候,首先会判断原来用于存储字符串的values的字符串数组有没有足够的大小来存储将要新添加入StringBuilder的字符串。如果不够用,那么就调用expandCapacity(int minimumCapacity)让容量翻两倍(一般是扩大两倍,特殊情况见代码),如果够用,那么就直接添加进去。

/**
* This implements the expansion semantics of ensureCapacity with no
* size check or synchronization.
*/
void expandCapacity(int minimumCapacity) {
int newCapacity = (value.length + 1) * 2; if (newCapacity < 0) {
newCapacity = Integer.MAX_VALUE;
} else if (minimumCapacity > newCapacity) {
newCapacity = minimumCapacity;
} value = Arrays.copyOf(value, newCapacity);
}

(3)StringBuffer

StringBuffer的构造函数

/**
* Constructs a string buffer with no characters in it and an
* initial capacity of 16 characters.
*/
public StringBuffer() {
super(16);
} /**
* Constructs a string buffer with no characters in it and
* the specified initial capacity.
*
* @param capacity the initial capacity.
* @exception NegativeArraySizeException if the <code>capacity</code>
* argument is less than <code>0</code>.
*/
public StringBuffer(int capacity) {
super(capacity);
}

StringBuffer也是直接调用父类(AbstractStringBuilder)的构造函数,那么我们从上面的分析中,就可以知道StringBuffer其实也是利用char[]类型的数组来保存字符串数组的。

再看看StringBuffer的append函数

public synchronized StringBuffer append(String str) {
super.append(str);
return this;
}

还是调用父类的append函数,但是在这里有值得注意的地方,StringBuffer的append函数有一个synchronized标识符,也就是说StringBuffer中的append函数是线程安全的,通过继续查阅其他StringBuffer中的函数,我们也可以发现他们有synchronized标识符,这就不难理解为什么StringBuffer是线程安全的,但是很明显加上线程控制会拖慢程序运行的速度,所以如果不需要线程控制,那么最好就用StringBuilder。

//下面只是节选一些StringBuffer中的函数
synchronized StringBuffer append(char ch)
synchronized StringBuffer append(char[] chars)
synchronized StringBuffer append(char[] chars, int start, int length)
synchronized StringBuffer append(Object obj)
synchronized StringBuffer append(String string)
synchronized StringBuffer append(StringBuffer sb)
synchronized StringBuffer append(CharSequence s)
synchronized StringBuffer append(CharSequence s, int start, int end)
synchronized StringBuffer insert(int index, char ch)
synchronized StringBuffer insert(int index, char[] chars)
synchronized StringBuffer insert(int index, char[] chars, int start, int length)
synchronized StringBuffer insert(int index, String string)
StringBuffer insert(int index, Object obj)

后记:

可能很多同学会想了解String中的+和StringBuilder.append的效率,以及纠结要用哪个。我在网上发现有人已经写了一篇文章,分享给大家在Java中连接字符串时是使用+号还是使用StringBuilder

参考链接

在Java中连接字符串时是使用+号还是使用StringBuilder

String详解, String和CharSequence区别, StringBuilder和StringBuffer的区别 (String系列之1)

从源代码的角度聊聊java中StringBuffer、StringBuilder、String中的字符串拼接的更多相关文章

  1. Java中 StringBuffer StringBuilder String 区别

    String       字符串常量   不可变  使用字符串拼接时是不同的2个空间 StringBuffer  字符串变量   可变   线程安全  字符串拼接直接在字符串后追加 StringBui ...

  2. 简单的理解 StringBuffer/StringBuilder/String 的区别

    StringBuffer/StringBuilder/String 的区别 这个三类之间主要的区别:运行速度,线程安全两个方面. 速度方面(快到慢): StringBuilder > Strin ...

  3. java中StringBuffer与String、StringBuilder的区别

    在java中我们经常可以看到StringBuffer和String的用法,但是我自己在使用过程中,经常会将两者弄混淆,今天我们就来了解一下两者的区别: 我们首先来看一下我们的官方API中的简单介绍: ...

  4. mysql下的将多个字段名的值复制到另一个字段名中(批量更新数据)字符串拼接cancat实战例子

    mysql下的将多个字段名的值复制到另一个字段名中(批量更新数据)mysql字符串拼接cancat实战例子: mysql update set 多个字段相加,如果是数字相加可以直接用+号(注:hund ...

  5. mybatis中使用mysql的模糊查询字符串拼接(like)

    方法一: <!-- 根据hid,hanme,grade,模糊查询医院信息--> 方法一: List<Hospital> getHospitalLike(@Param(" ...

  6. StringBuffer StringBuilder String 区别

    String       字符串常量   不可变  使用字符串拼接时是不同的2个空间 StringBuffer  字符串变量   可变   线程安全  字符串拼接直接在字符串后追加 StringBui ...

  7. JAVA (StringBuffer/StringBuilder)常用API

    public class Copy3 { public static void main(String[] args) { //构造实例化 StringBuffer strbu = new Strin ...

  8. sql中的for xml path() 实现字符串拼接

       通常我们需要在sql中拼接字符串   ,可以用for xml path() 来进行拼接,如下实例. 同时未去掉最后一个逗号可以用LEFT函数来实现.     ) AS UserName  FRO ...

  9. FOR XML PATH做为数据表中单列或者多列的字符串拼接的方法,放到一列中去,很好用。

    先看看自己弄得例子,SELECT sName+',',hoppy+','  FROM student2 where hoppy='游泳' FOR XML PATH('')--PATH后面跟的是行标题, ...

随机推荐

  1. MySQL计算销售员昨日各指标综合得分_20161206

    数据需求如上,新增重激活以15天未下单为界定 SELECT d.销售员,(日销售额得分*0.6+日新增客户数得分*0.15+日客单价得分*0.1+日客户平均产品数得分*0.15)AS 综合得分,日销售 ...

  2. NGUI ScrollView总结

    1顶级父类加boxCollider和UIDragScrollView脚本保证在item拖走后也能通过拖拽剩余区域的方式操作. 2下一级添加UIPanel在Clipping设定区域,注意如果设定soft ...

  3. VC++双缓冲保持背景不擦除之实现

    几天前,我终于克服了C++窗体重绘时的闪烁问题,用到的技巧就是双缓冲.但是怎样保持住已经绘制的图形呢?也就是仿照Windows自带的画图程序一般,动态的做出一条直线.最容易想到的方法是在MouseMo ...

  4. 使用C# WinForm窗体制作经理评分项目 ——S2 2.2

    在窗口加载时初始化三个员工对象 用数组存放 这是员工类的大致字段和属性. 在FrmMain中给对象数组附初值 以上 FrmMain中用一个ListView控件展示员工信息,通过以上代码将对象数组中的内 ...

  5. Linq查询数据集取得排序后的序列号(行号)

    今天群里有同学问如何用linq取结果集的行号,查了一下资料,发现linq有很简单的方法可以实现,花了几分钟写了一个测试用例,现记录下来,以备参考: /// <summary> /// 测试 ...

  6. 用python监控Linux,CPU,内存,硬盘

    #!/usr/local/bin/python3.5 #coding:utf-8 import mailll, linecache, re, socket, os, time hostname = s ...

  7. Java 基础知识总结 (一、标识符)

    一.Identifiers: 标识符 Names of class,method and variable 用于类名.方法名.变量名 Begin with character,'_' or '$' 标 ...

  8. iOS工作笔记(十三)

    1.automaticallyAdjustsScrollViewInsets的使用 这是UIViewController的属性,设置为YES就是根据status bar,navigation bar, ...

  9. AjaxControlToolkit MaskedEdit Unspecified error 未指定错误

    使用AjaxControlToolkit 里面的 MaskedEditValidator控件,IE里面在如下的js中出现未指定(Unspecified error)错误, if (document.a ...

  10. MVC 路由模块内核原理

    .net网站第一次运行的时候 执行global文件的application_start方法 注册路由信息   RouteConfig.RegisterRoutes(RouteTable.Routes) ...