string.h java_Java 基础 之 String

Java 基础 之 String

在 Java 中字符串的使用非常广泛, 我们常用的如: String ,StringBuffer, StringBuilder. 但是一直都没有系统的整理过他们的区别, 今天就系统的整理一下, 记录自己的学习历程.

一: String

首先我们看下 String 的源码:

public final class String implementsjava.io.Serializable, Comparable, CharSequence

从 String 的类声明上就可以发现, String 是一个类而不是基本数据类型.

既然是一个类, 那 String 的使用也很简单, 直接 new 就可以了.

String 可以通过 "+" 来完成字符串的拼接, 那我们来做个测试:

ab7653affab982b574eb7acc55df2e04.gif

使用是很简单, 但是通过我们上面的测试发现一个问题就是, 当我们 给 str + "test" 后, 他的 hashCode 发生了变化, 说明了第 10 行的 str 已经是一个新的对象了.

在 String 的源码中维护了一个 finalchar value[], 如下:

private final charvalue[];

所以从源码中我们可以发现, value 是 final 类型的, 是不能被改变的, 所以只要改变就只能生一个新的 String 对象. 也可以说 String 类型的对象是长度不可变的, String 拼接字符串每次都要生成一个新的对象, 所以拼接字符串的效率也比较低.

二: StringBuffer

首先我们看下 StringBuffer 的源码:publicfinalclassStringBufferextendsAbstractStringBuilder

implementsjava.io.Serializable,CharSequence

从源码中我们可以看出, StringBuffer 也是一个 final class, 也不能被继承. 既然是类, 那使用也一样直接 new 一个对象出来.

StringBuffer 是通过. append 来完成字符串的拼接, 那让我们来做个测试

ab7653affab982b574eb7acc55df2e04.gif

从我们的测试来看, StringBuffer 和 String 类是不同的, 因为 StringBuffer 被修改后并没有产生新的对象, 是在之前的对象上修改的. 下面我们来看下部分源码:publicStringBuffer(){

super(16);

}

publicStringBufferappend(CharSequences){

if(s==null)s="null";

if(s instanceofString)returnthis.append((String)s);

if(s instanceofStringBuffer)returnthis.append((StringBuffer)s);

returnthis.append(s,0,s.length());

}

publicsynchronizedStringBufferappend(CharSequences,intstart,intend)

{

super.append(s,start,end);

returnthis;

}

从上面的代码中可以看出来, StringBuffer 的初始容量可以容纳 16 个字符, 当该对象的实体存放的字符的长度大于 16 时, 实体容量就自动增加.

StringBuffer 的函数都是加了 Synchronized 关键字的, 所以 StringBuffer 的方法是线程安全的, 可以在多线程中使用.

总结:

如果对字符串的改变少, 使用 String;

如果对字符串修改的较多或需要线程安全就用 StringBuffer,

三: StringBuilder

首先我们看下 StringBuilder 的源码:publicfinalclassStringBuilderextendsAbstractStringBuilder

implementsjava.io.Serializable,CharSequence

从源码中我们可以看出, StringBuilder 和 StringBuffer 一样, 也是一个 final class, 也不能被继承. 既然是类, 那使用也一样直接 new 一个对象出来.

StringBuilder 是通过. append 来完成字符串的拼接, 那让我们来做个测试

ab7653affab982b574eb7acc55df2e04.gif

从上面的测试我门可以发现 StringBuilder 类的对象能够被多次的修改, 并且不产生新对象.

下面我们来看下部分源码:publicStringBuilder(Stringstr){

super(str.length()+16);

append(str);

}

privateStringBuilderappend(StringBuildersb){

if(sb==null)returnappend("null");

intlen=sb.length();

intnewcount=count+len;

if(newcount>value.length)

expandCapacity(newcount);

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

count=newcount;

returnthis;

}

从源码中我们可以发现它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问). 所以在单线程的环境下 StringBuilder 相较于 StringBuffer 有速度优势, 因为它不需要做同步处理.

StringBuffer 的默认长度是 16, StringBuilder 的默认长度是 16 + 初始化传入字符串的长度.

四: 扩容

下面是扩容的源码:voidexpandCapacity(intminimumCapacity){

intnewCapacity=value.length*2+2;

if(newCapacity-minimumCapacity<0)

newCapacity=minimumCapacity;

if(newCapacity<0){

if(minimumCapacity<0)// overflow

thrownewOutOfMemoryError();

newCapacity=Integer.MAX_VALUE;

}

value=Arrays.copyOf(value,newCapacity);

}

当我们对 StringBuffer 和 StringBuilder 进行 append 的时候, 会先判断当前的容量是否可以放下, 如果长度不够, 就会调用 expandCapacity 来进行扩容

扩容的计算公式: 现在的长度 * 2 + 2;

如果我们扩容后的长度 (newCapacity) 还是放不下 (minimumCapacity) 的长度那就直接把所需要的长度赋值给扩容后的长度(newCapacity = minimumCapacity);

因为当 int 的最大值 + 1 就会变成负数, 所以我们需要在扩容完后验证, newCapacity 的正确性防止内存溢出, 如果超过了 int 的最大长度, 那么就把 NewCapacity 设置成 Integer.MAX_VALUE,

newCapacity = Integer.MAX_VALUE;

ab7653affab982b574eb7acc55df2e04.gif

最后把原数组 copy 到扩容后的数组中:

value = Arrays.copyOf(value,newCapacity);

总结:

当需要频繁的字符串拼接和删除时, 建议使用 StringBuffer 或 StringBuilder

在单线程的程序中, 使用 String 或 StringBuilder

在多线程的程序中, 使用 StringBuffer.

来源: http://www.jianshu.com/p/b8cfa52be50c

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值