Android进阶–String、StringBuffer、StringBuilder的理解
问题:
理解 Java的字符串,String、StringBuffer、StringBuilder 有什么区别?
知识点
- 字符串设计和实现考量
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;
|
- 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); } }
|
- 字符串缓存
String在Java 6 以后提供了intern()方法,目的是提示JVM把相应字符串缓存起来,以便重复使用。
注意:Java 6这种历史版本,并不推荐大量使用intern(),因为缓存的字符串是存在“永久代”中,这个空间比较有限。也基本不会被FULLGC之外的垃圾收集照顾到。所以,使用不当,容易OOM。后续版本中,被放到堆中,设置永久代在Java 8中被元数据区替代了。
intern()感兴趣可以参考文章:Java提高篇——理解String 及 String.intern() 在实际中的应用
- 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(); }
|
参考:
声明:此为原创,转载请联系作者
作者:微信公众号添加公众号-遛狗的程序员 ,或者可以扫描以下二维码关注相关技术文章。
当然喜爱技术,乐于分享的你也可以可以添加作者微信号: