String、StringBuffer、StringBuilder的理解

问题:

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

知识点
  1. 字符串设计和实现考量 String是Immutable(线程安全、字符串常量池复用)。Immutable对象在拷贝时候不需要额外复制数据。至于为什么imumutable,源码如下:
//关键点 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。相关源码如下:
//AbstractStringBuilder 没有加final 也没private修饰
abstract class AbstractStringBuilder implements Appendable, CharSequence {
    char[] value;
    int count;
    private static final int MAX_ARRAY_SIZE = 2147483639;

    AbstractStringBuilder() {
    }
复制代码
//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;
    }
复制代码
//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 适用于常量声明。如:

 public static final int DEVICE_NOT_AVAILABLE_ERROR_CODE = 390004;// 设备未启用或者被锁住
复制代码

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

/**
     * 获取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的封装。

/**
         * 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();
        }

复制代码

参考:

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


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

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

转载于:https://juejin.im/post/5b5988a15188251af121f38d

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值