String、StringBuffer和StringBuilder的使用方法

前言

让客户满意是我们工作的目标,不断超越客户的期望值来自于我们对这个行业的热爱。我们立志把好的技术通过有效、简单的方式提供给客户,将通过不懈努力成为客户在信息化领域值得信任、有价值的长期合作伙伴,公司提供的服务项目有:主机域名、网络空间、营销软件、网站建设、玉泉街道网站维护、网站推广。

相信很多程序员在使用String、StringBuilder和StringBuffer的时候都知道怎么使用,却并不会去看其原理,

在学习这三个类之前先认识一下CharSequence接口和Appendable接口:

CharSequence接口,出自于JDK1.4,有如下几个常用的方法:

int length(); 返回字符序列长度

char charAt(int index); 返回字符序列在指定索引处的字符

CharSequence subSequence(int start, int end); 截取字符序列从索引start到end之间的值。包括start不包括end。例如:长度为5的字符序列“12345”,截图0到3的值为“123”,即真正的返回值为索引start到end-1之间的值。

Appendable接口,出自JDK1.5,有如下几个常用方法:

Appendable append(CharSequence csq) throws IOException; 拼接字符序列

Appendable append(CharSequence csq, int start, int end) throws IOException; 拼接字符序列指定区间内的字符序列,包括start不包括end。即真正拼接的值为索引start到end-1之间的值。

Appendable append(char c) throws IOException; 拼接字符

概括

String类是字符串常量,是不可更改的常量。而StringBuffer是字符串变量,它的对象是可以扩充和修改的。

 StringBuffer类的构造函数

 public StringBuffer()

 创建一个空的StringBuffer类的对象。

 public StringBuffer( int length )

 创建一个长度为 参数length 的StringBuffer类的对象。

 注意:如果参数length小于0,将触发NegativeArraySizeException异常。

 public StringBuffer( String str )

 用一个已存在的字符串常量来创建StringBuffer类的对象。

StringBuffer类

StringBuffer类,继承AbstractStringBuilder,实现Serializable序列化,操作上是线程安全的,线程安全的原因就是该类进行数据操作的相关方法都加了synchronized关键字,而StringBuilder则都没有加。

toStringCache变量:使用transient修饰,不参与序列化,作为toString方法缓存用

默认构造器:调用父类构造器,传递一个默认的长度值16,父类构造器创建一个长度为16的字符数组;

参数为int的构造器:参数为int的构造器意思是构造一个指定长度的字符数组

参数为String和CharSequence的构造器:会构造一个长度为(16+CharSequence字符长度)或者(16+String字符串长度)的字符数组。然后再拼接一下参数中的字符或者字符串,如果参数为null会抛出空指针异常(NullPointerException)。

length方法:返回实际数据长度

capacity方法:返回父类构造器的字符数组长度

public final class StringBuffer extends AbstractStringBuilder

implements java.io.Serializable, CharSequence{

private transient char[] toStringCache;

/** use serialVersionUID from JDK 1.0.2 for interoperability */

static final long serialVersionUID = 3388685877147921107L;

public StringBuffer() {

super(16);

}

public StringBuffer(int capacity) {

super(capacity);

}

public StringBuffer(String str) {

super(str.length() + 16);

append(str);

}

public StringBuffer(CharSequence seq) {

this(seq.length() + 16);

append(seq);

}

@Override

public synchronized int length() {

return count;

}

@Override

public synchronized int capacity() {

return value.length;

}

接下来看一看一些相关操作的方法:

可以看到如拼接,插入,删除,替换之类的操作都是调用父类的方法,并且返回的是调用该方法的类对象,这就表明StringBuffer类在进行数据改变的操作后返回类对象本身,原因就是父类中储存数据的字符数组value没有final所修饰,所以可以做到修改数据而不改变类对象。

而String类中储存数据的字符数组变量value是被final修饰的,也就说明无法对String类对象的值进行直接的修改,所以对其进行数据改变操作的返回值都是new String(XXX),也就是每个返回值都是一个新的String对象,所以String类不适合大量的数据值改变的操作。

toStringCache变量:只有在调用toString方法的时候给予赋值操作,临时储存数据,然后转换为Sting对象当做返回值。而每次对数据进行改变的时候都会重置变量值,保证每次toString之前该变量都是空。

@Override

public synchronized StringBuffer append(String str) {

toStringCache = null;

super.append(str);

return this;

}

@Override

public synchronized StringBuffer delete(int start, int end) {

toStringCache = null;

super.delete(start, end);

return this;

}

@Override

public synchronized StringBuffer replace(int start, int end, String str) {

toStringCache = null;

super.replace(start, end, str);

return this;

}

@Override

public synchronized StringBuffer insert(int index, char[] str, int offset,

int len)

{

toStringCache = null;

super.insert(index, str, offset, len);

return this;

}

@Override

public int indexOf(String str) {

// Note, synchronization achieved via invocations of other StringBuffer methods

return super.indexOf(str);

}

@Override

public synchronized int indexOf(String str, int fromIndex) {

return super.indexOf(str, fromIndex);

}

@Override

public int lastIndexOf(String str) {

// Note, synchronization achieved via invocations of other StringBuffer methods

return lastIndexOf(str, count);

}

@Override

public synchronized int lastIndexOf(String str, int fromIndex) {

return super.lastIndexOf(str, fromIndex);

}

@Override

public synchronized StringBuffer reverse() {

toStringCache = null;

super.reverse();

return this;

}

@Override

public synchronized String toString() {

if (toStringCache == null) {

toStringCache = Arrays.copyOfRange(value, 0, count);

}

return new String(toStringCache, true);

}

StringBuilder类

StringBuilder类和StringBuffer差不多,少了一个toStringCache变量,所以的操作方法都没有添加sybchronized关键字,所以StringBuilder是线程不安全的类。就不多说了哈(*^▽^*)。

AbstractStringBuilder

AbstractStringBuilder产于JDK1.5,实现Appendable接口和CharSequence接口

该类只能被继承,有两个子类StringBuffer和StringBuilder,会默认调用有参构造器,指定初始化的字符串数据长度

value: 实例化时创建出来的字符数组

count: 实际数据包含的字符的长度

length方法:返回实际数据包含的字符的长度

capacity方法:返回字符数组的大小

代码如下:

abstract class AbstractStringBuilder implements Appendable, CharSequence {

char[] value;

int count;

AbstractStringBuilder() {

}

AbstractStringBuilder(int capacity) {

value = new char[capacity];

}

@Override

public int length() {

return count;

}

public int capacity() {

return value.length;

}

数据储存空间操作相关方法:

ensureCapacityInternal:每次进行数据改变操作之前都会调用的方法,其意在于确保数组变量value有能力承受接下来的改变,说白了就是对数据操作的之前改变字符数组大小,使其容量能足够接下来的操作使用而不出现错误。

MAX_ARRAY_SIZE:私有静态常量,值为 Integer.MAX_VALUE - 8,按照文档翻译来说,就是字符数组的最大容量,但是却没有达到Integer的最大值,原因是某些JVM虚拟机会在一个数组中保留一些标题词,如果强行尝试区分配超过这个容量的数组可能会导致抛出异常OutOfMemoryError:请求的数组大小超过VM限制。

newCapacity:重新设置字符数组的容量,并作为返回值。参数表示字符数组的最小容量,首先要知道字符串数组初始长度为16,扩容方式为原字符数组长度左移一位后再加2(原字符数组长度乘以2,再加2)。该方法就是比较一次扩容后value长度值newCapacity和参数指定的最小值minCapacity,如果一次扩容满足最小值需求,则使用newCapacity,如果不满于则直接使用minCapacity并且赋值于newCapacity,最后判断minCapacity的大小是否大于0并小于指定的MAX_ARRAY_SIZE的值,如果满足则返回minCapacity的值,如果不满足则表示要求的最小值超过了建议的最大值容量,那将把minCapacity传递给为hugeCapacity方法,并以该方法的返回值作为本方法的返回值,如下。郑州专业妇科医院 http://www.hnzzkd.com/

hugeCapacity:如果参数给定值超过Integer的类型最大值,抛出内存溢出异常,如果小于Integer的最大值,则和AbstractStringBuilder类的建议值对比,哪一个值大,则使用哪一个值作为返回值。

trimToSize:去除多余的数组储存空间,提高空间利用率。比较数组空间value的大小和实际数据count大小,如果实际数据元素小于value,则表示实际数据并未占满分配的空间,调用Arrays.copyOf方法把value中的空间铺满,返回铺满后的value值,

setLength:设置当前序列的长度为指定的参数长度newLength,如果当前序列的长度超出指定长度newLength,就把已有序列的前面长度为newLength的字符拷贝到新字符序列里,多出来的一部分舍弃。如果不超过newLegth,原有数据不变,就把缺少的几个位置的数据设置为空字符。

private void ensureCapacityInternal(int minimumCapacity) {

// overflow-conscious code

if (minimumCapacity - value.length > 0) {

value = Arrays.copyOf(value,

newCapacity(minimumCapacity));

}

}

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

private int newCapacity(int minCapacity) {

// overflow-conscious code

int newCapacity = (value.length << 1) + 2;

if (newCapacity - minCapacity < 0) {

newCapacity = minCapacity;

}

return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)

? hugeCapacity(minCapacity)

: newCapacity;

}

private int hugeCapacity(int minCapacity) {

if (Integer.MAX_VALUE - minCapacity < 0) { // overflow

throw new OutOfMemoryError();

}

return (minCapacity > MAX_ARRAY_SIZE)

? minCapacity : MAX_ARRAY_SIZE;

}

public void trimToSize() {

if (count < value.length) {

value = Arrays.copyOf(value, count);

}

}

public void setLength(int newLength) {

if (newLength < 0)

throw new StringIndexOutOfBoundsException(newLength);

ensureCapacityInternal(newLength);

if (count < newLength) {

Arrays.fill(value, count, newLength, '\0');

}

count = newLength;

}

接下来看一下实际的数据操作,这里以最常用的拼接和替换为例:

appendNull:先扩容,然后依次拼接字符n u l l,拼接的同时进行count++;

append:先判断字符串是否为null,如果是执行appendNull,如果不是空,则获取目标对象的长度,然后进行扩容,然后把目标对象拷贝到value的后面。然后更新数据的大小count。返回类对象

replace:先判断一系列索引越界问题,如果都没问题检查end是否超出count,超出了则修改end的值为count,获取目标字符串的长度,计算经过替换后的数据长度。原长度减去要被替换的长度加上要替换成的字符串长度:count-(end-start)+len。计算完成后更新字符数组大小,拷贝字符后更新count的值。返回类对象

private AbstractStringBuilder appendNull() {

int c = count;

ensureCapacityInternal(c + 4);

final char[] value = this.value;

value[c++] = 'n';

value[c++] = 'u';

value[c++] = 'l';

value[c++] = 'l';

count = c;

return this;

}

public AbstractStringBuilder append(String str) {

if (str == null)

return appendNull();

int len = str.length();

ensureCapacityInternal(count + len);

str.getChars(0, len, value, count);

count += len;

return this;

}

public AbstractStringBuilder replace(int start, int end, String str) {

if (start < 0)

throw new StringIndexOutOfBoundsException(start);

if (start > count)

throw new StringIndexOutOfBoundsException("start > length()");

if (start > end)

throw new StringIndexOutOfBoundsException("start > end");

if (end > count)

end = count;

int len = str.length();

int newCount = count + len - (end - start);

ensureCapacityInternal(newCount);

System.arraycopy(value, end, value, start + len, count - end);

str.getChars(value, start);

count = newCount;

return this;

}

public AbstractStringBuilder delete(int start, int end) {

if (start < 0)

throw new StringIndexOutOfBoundsException(start);

if (end > count)

end = count;

if (start > end)

throw new StringIndexOutOfBoundsException();

int len = end - start;

if (len > 0) {

System.arraycopy(value, start+len, value, start, count-end);

count -= len;

}

return this;

}


新闻名称:String、StringBuffer和StringBuilder的使用方法
转载注明:http://myzitong.com/article/pjhjss.html