String,Stringbuffer,Stringbuilder

  • String

定义:

public final class String
    implements java.io.Serializable, Comparable, CharSequence {

可以看出 String 是 final 类型的,表示该类不能被其他类继承,同时该类实现了三个接口:java.io.Serializable Comparable CharSequence

属性:

private final char value[];

这是一个字符数组,并且是 final 类型,用于存储字符串内容。从 final 关键字可以看出,String 的内容一旦被初始化后,其不能被修改的。
所以对String的修改都是将引用指向了新的字符串。

构造方法:

String可以用byte[],String,StringBuffer,StringBuilder,char[]构造
其中用byte[]构造的时候,涉及到编码问题,编码用Charset指定。
其中还有一个构造方法要注意:

String(char[] value, boolean share) {
        // assert share : "unshared not supported";
        this.value = value;
    }

这个构造方法是把char[]的引用直接给String内部的char[],而其他构造方法则是new一个char[],值复制。这个是一个protected方法,所以不用担心安全问题。

其他方法:

String的其他方法在api文档直接查就可以了,这里我就不列举了。
由于String的特性,对String有修改的方法都是返回一个新的String对象。

编码问题

之前提到的String内部是用一个char[]来存储的。而char是unicode编码,是两个字节存一个字符。
但是用getbytes()方法获得的byte[]的长度并不是char[]长度*2。原因是getbytes()方法获得的byte[]并不是unicode编码。String类型的默认编码方式是和本地编码方式相关。编码方式也可以指定。

之所以提这个是因为我之前写的代码需要把固定长度的String转换为固定长度的byte[],但是GBK,UTF-8,UTF-16编码都是不等长编码,而iso8859-1编码不支持中文。而在charest的编码集里没有找到等长编码的unicode编码。
最后我用toCharArray()获得String内部的char[]的副本,因为char是unicode编码,所以我把char[]转换成了char[]长度*2的byte[]数组。

String常量池与字符串拼接性能优化

String常量池

String常量池是一个初始为空的字符串池,它由类 String 私有地维护。

String s1="hanhan";
String s2="hanhan";
System.out.println(s1==s2);//true

当我们创建String对象采用字面量形式时,JVM首先会对这个字面量进行检查,如果常量池中存放有该字面量,则直接使用,否则创建新的对象并将其引用放入常量池中。

String s1="gh";
String s2=new String("gh");
s2.intern();
System.out.println(s1==s2);//false;
s2=s2.intern();
System.out.println(s1==s2);//true;

当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(equals(Object)方法确定),则返回池中的字符串。否则,将此 String 对象添加到池中,并返回此 String 对象的引用。

常量池中存的是对对象的引用,存储于JVM的方法区中,而且引用的对象存储于堆中。

当常量池中的引用没有被任何变量引用时,就会被GC回收!

String常量池
String s1="a"+"b"+"c";

正常情况下,执行声明s1,代码会生成3个对象,即对象a、对象ab、对象abc,其中对象a和对象ab都是中间的临时变量,最后的对象abc才赋值给了s1。因此在使用字符串拼接的时候,拼接的数量越多,性能越低!
但是java编译器在编译的时候做了优化,在编译时新建一个对象StringBuilder来拼接,这样就避免了产生很多临时对象,从而提升了性能!

String s="";
for(int i=0;i

但是这个做法效率会很低。因为循环内,每次都在做字符串拼接,每次都在产生一个StringBuilder对象,造成内存的浪费!因此这种错误要尽量避免,稍做以下优化即可完美改造:

String s="";
StringBuilder sb=new StringBuilder();
for(int i=0;i
  • Stringbuffer与Stringbuilder

在线程安全上,StringBuilder是线程不安全的,而StringBuffer是线程安全的。因为StringBuffer的大部分方法带有synchronized修饰符,而StringBuilder没有。

StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况

这两个类的大多数方法都是继承自父类AbstractStringBuilder。或者做了少量的应用。所以要了解这两个类,应该去了解父类AbstractStringBuilder

/**
     * 底层存储的字符数组
     */
    char[] value;

    /**
     * 用于记录存的字符的数目
     */
    int count;

从它的成员变量我们可以发现和ArrayList的有些类似,事实上看过源码之后你会发现他们的底层机制是很类似的。

我们主要研究append()方法,以append(String str)为例

public AbstractStringBuilder append(String str) {
        if (str == null) str = "null";
        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }

其实就是把str的内容复制添加到内部的char数组value的末尾。
关键是ensureCapacityInternal(count + len);这行代码。它的作用是检查value的容量够不够,不够的话就扩充,这个和ArrayList的扩充机制基本一致,看过源码后你会发现,连写法都差不多。

int newCapacity = value.length * 2 + 2;

新的容量是旧容量的2倍+2,默认容量是16。

你可能感兴趣的:(String,Stringbuffer,Stringbuilder)