Java还要再学一遍基础(五)String,StringBuilder,StringBuffer

来源:互联网 发布:云内动力网络商学院 编辑:程序博客网 时间:2024/04/30 07:44

String,StringBuilder,StringBuffer学习

String,StringBuilder,StringBuffer的话题也是老生长谈了。权当记录一下。

1. String相关

  • 常量池:在编译期被确定,保存在.class文件中的一些常量,其中就有字符串。
  • String不属于Java的基本数据类型。是对象。
  • String被创建的时候会先去检查字符常量池里面有没有这个字符串,有的话直接使用,没有的话在字符常量池里面创建一个,再返回地址。
        String s1 = "what";        String s2 = "what";        System.out.println(s1 == s2);

上面的代码输出:

true

s1会被创建,s2直接使用s1中的地址。

  • 使用new String()的时候会先在对内存中申请一块区域存储对象,再去检查字符常量池是否有这个字符串。
        String s1 = "what";        String s2 = new String("what");;        System.out.println(s1 == s2);

输出:

false
  • 编译期不能确定的情况
        String s1 = "what0";        String s2 = new String("what0");;        int q = 0;        String s3 = "what" + q;        System.out.println(s1 == s2);        System.out.println(s1 == s3);

输出:

falsefalse

因为q在编译期是无法被确定的所以返回false;

  • String的intern方法
    public native String intern();

一个本地方法,返回这个String对象在常量池中的引用。

        String s1 = "what0";        String s2 = new String("what0");;        int q = 0;        String s3 = "what" + q;        System.out.println(s1 == s2.intern());        System.out.println(s1 == s3.intern());

输出:

truetrue

比较的是常量池中的同一个引用,所以都是返回的true

因位字符串一旦改变就会重新创建,所以效率很低。

2. StringBuilder相关

  • 类定义
public final class StringBuilder    extends AbstractStringBuilder    implements java.io.Serializable, CharSequence

和StringBuffer都继承自AbstractStringBuilder类(String的变换相关的操作都被封装在里面,append,insert等)。同时实现序列化接口和字符序列接口。

  • 重要的属性
    /**     * The value is used for character storage.     */    char[] value;    /**     * The count is the number of characters used.     */    int count;

实际上StringBuilder和StringBuffer都是用char[] 字符数组实现的。

  • 关键的构造器
    public StringBuilder() {        super(16);    }    public StringBuilder(int capacity) {        super(capacity);    }    public StringBuilder(String str) {        super(str.length() + 16);        append(str);    }

父类对应的构造器:

AbstractStringBuilder(int capacity) {        value = new char[capacity];    }

默认的容量是16,也就是能容纳16个字符。同时也可以指定容量。

  • append方法
    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;    }

调用的是父类的方法,同时其中很重要的就是当容量不够的时候的处理:

    private void ensureCapacityInternal(int minimumCapacity) {        // overflow-conscious code        if (minimumCapacity - value.length > 0) {            value = Arrays.copyOf(value,                    newCapacity(minimumCapacity));        }    }    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;    }

这里着重关注的当然是newCapacity()方法

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

默认的增长的容量为原容量的2倍加上2

        StringBuilder builder = new StringBuilder();        System.out.println(builder.capacity());        builder.append("01234567890123456");        System.out.println(builder.capacity());

输出:

1634

再看一下下面这一段代码:

        StringBuilder builder = new StringBuilder();        System.out.println(builder.capacity());        builder.append("012345678901234");        System.out.println(builder.capacity());        builder.append("01234567890123456789");        System.out.println(builder.capacity());

输出:

161635

这里第二次append的时候并不是直接扩容2倍再+2,而是刚好35。

if (newCapacity - minCapacity < 0) {            newCapacity = minCapacity;        }

原来:newCapacity - minCapacity也就是34 - 35是小于零的所以直接把35作为了新的容量。

所以并不是一定单纯的原来的容量*2 + 2,如果原来的容量*2 + 2还是不够的话就直接使用目标容量。

3. StringBuffer相关

  • StringBuffer的实现原理和StringBuilder是基本一样的。只是里面使用了多个synchronized来实现同步而已。但是相对效率上就会低一些。所以当涉及到并发的时候可以使用StringBuffer,单线程情况下StringBuilder效果更佳