Android进阶–String、StringBuffer、StringBuilder的理解

问题:

理解 Java的字符串,String、StringBuffer、StringBuilder 有什么区别?

知识点
  1. 字符串设计和实现考量
    String是Immutable(线程安全、字符串常量池复用)。Immutable对象在拷贝时候不需要额外复制数据。至于为什么imumutable,源码如下:
1
2
3
4
5
6
7
8
9
//关键点 final
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
// The associated character storage is managed by the runtime. We only
// keep track of the length here.
//关键点final private
// private final char value[];
private final int count;
  1. StringBuffer、StringBuilder底层都是利用可修改的数组(JDK 9之后是byte)数组,都继承了AbstractStringBuilder,里面包含了基本操作。区别StringBuffer增加了synchronized。相关源码如下:
1
2
3
4
5
6
7
8
//AbstractStringBuilder 没有加final 也没private修饰
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value;
int count;
private static final int MAX_ARRAY_SIZE = 2147483639;
AbstractStringBuilder() {
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//StringBuilder截取部分源码
static final long serialVersionUID = 4383685877147921099L;
public StringBuilder() {
super(16);
}
public StringBuilder(int var1) {
super(var1);
}
public StringBuilder(String var1) {
super(var1.length() + 16);
this.append(var1);
}
public StringBuilder(CharSequence var1) {
this(var1.length() + 16);
this.append(var1);
}
public StringBuilder append(Object var1) {
return this.append(String.valueOf(var1));
}
public StringBuilder append(String var1) {
super.append(var1);
return this;
}
public StringBuilder append(StringBuffer var1) {
super.append(var1);
return this;
}
public StringBuilder append(CharSequence var1) {
super.append(var1);
return this;
}
public StringBuilder append(CharSequence var1, int var2, int var3) {
super.append(var1, var2, var3);
return this;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//StringBuffer部分源码 多了synchronized
public StringBuffer(CharSequence seq) {
this(seq.length() + 16);
append(seq);
}
public synchronized int length() {
return count;
}
public synchronized int capacity() {
return value.length;
}
public synchronized void ensureCapacity(int minimumCapacity) {
if (minimumCapacity > value.length) {
expandCapacity(minimumCapacity);
}
}
  1. 字符串缓存
    String在Java 6 以后提供了intern()方法,目的是提示JVM把相应字符串缓存起来,以便重复使用。

    注意:Java 6这种历史版本,并不推荐大量使用intern(),因为缓存的字符串是存在“永久代”中,这个空间比较有限。也基本不会被FULLGC之外的垃圾收集照顾到。所以,使用不当,容易OOM。后续版本中,被放到堆中,设置永久代在Java 8中被元数据区替代了。

intern()感兴趣可以参考文章:Java提高篇——理解String 及 String.intern() 在实际中的应用

  1. String自身也有相关优化
    有兴趣可以自己查看相关文章,主要是char数组换成了byte数组加上一个标志编码所谓的coder。
回答问题:
String的创建机理

由于String在Java世界中使用过于频繁,Java为了避免在一个系统中使用大量的Java对象,引入了字符串常量池的概念。其运行机制是:创建一个字符串的时候,首先检查池中是否有相等的字符串对象,如果有就不需要创建,直接从池中找到对象引用,如果没有的话,新建字符串对象,返回对象引用。但是,通过new方法创建的不会检查常量池是否存在,而是直接在堆中或者栈中创建一个新的对象,也不会把对象放入池中。上面所说的只适用于直接给String引用赋值的情况。

注意:String是immutable
Strng提供了inter()方法可以将Strng对象添加到池中,并且返回该对象的引用。(如果由equals()确定池中有该字符串,那就直接返回)。

当两个String对象拥有相等的值的时候,他们只引用字符串中同一个拷贝。当同一个字符串大量出现的时候,可以大量节省内存空间。

StringBuffer/StringBuilder

StringBuffer/StringBuilder相同点:

  • String/StringBuilder相对于String的话,他们值是可以改变的,并且值改变后,他们引用不会变。他们在构造的时候使用一个默认的数组,在后续加入数据后超过默认大小后会创建更大的数组,并且将原来数组的内容复制过来,再丢弃旧的数组。因此,项目开发的时候,对于较大对象的扩容会性能,因此,能预估大小,最好不过。

StringBuffer/StringBuilder不同点:

  • 另外StringBuffer是线程安全(方法定义前面使用了synchronize),StringBuilder不是。StringBuffer性能要低于StringBuilder。
    应用场景:
    String 适用于常量声明。如:
1
public static final int DEVICE_NOT_AVAILABLE_ERROR_CODE = 390004;// 设备未启用或者被锁住

StringBuffer,适用于频繁进行字符串运算(如拼接、替换、删除等),并且运行在多线程环境下,建议使用,比如Http参数解析和封装。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* 获取POST请求的base url
*
* @param requestHolder
* @return
* @throws AlipayApiException
*/
private String getRequestUrl(RequestParametersHolder requestHolder) throws AlipayApiException {
StringBuffer urlSb = new StringBuffer(serverUrl);
try {
String sysMustQuery = WebUtils.buildQuery(requestHolder.getProtocalMustParams(),
charset);
String sysOptQuery = WebUtils.buildQuery(requestHolder.getProtocalOptParams(), charset);
urlSb.append("?");
urlSb.append(sysMustQuery);
if (sysOptQuery != null & sysOptQuery.length() > 0) {
urlSb.append("&");
urlSb.append(sysOptQuery);
}
} catch (IOException e) {
throw new AlipayApiException(e);
}
return urlSb.toString();
}

StringBuilder,适用于频繁进行字符串运算(如拼接、替换、删除等),并且运行在单线程环境下,建议使用,比如Json的封装。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* Extracts absolute path form a given URI. E.g., passing
* <code>http://google.com:80/execute?query=cat#top</code>
* will result in <code>/execute?query=cat#top</code>.
*
* @param uri URI which absolute path has to be extracted,
* @return the absolute path of the URI,
*/
private String getAbsolutePathFromAbsoluteURI(URI uri) {
String rawPath = uri.getRawPath();
String rawQuery = uri.getRawQuery();
String rawFragment = uri.getRawFragment();
StringBuilder absolutePath = new StringBuilder();
if (rawPath != null) {
absolutePath.append(rawPath);
} else {
absolutePath.append("/");
}
if (rawQuery != null) {
absolutePath.append("?").append(rawQuery);
}
if (rawFragment != null) {
absolutePath.append("#").append(rawFragment);
}
return absolutePath.toString();
}

参考:

声明:此为原创,转载请联系作者


作者:微信公众号添加公众号-遛狗的程序员 ,或者可以扫描以下二维码关注相关技术文章。

qrcode_for_gh_1ba0785324d6_430.jpg
当然喜爱技术,乐于分享的你也可以可以添加作者微信号:

WXCD.jpeg

文章目录
  1. 1. 问题:
    1. 1.0.1. 知识点
    2. 1.0.2. 回答问题:
      1. 1.0.2.1. String的创建机理
    3. 1.0.3. StringBuffer/StringBuilder
    4. 1.0.4. 应用场景:
|