01 String&StringBuffer&StringBuilder之间的区别

275 阅读6分钟

1,String

  • String类:代表字符串。Java 程序中的所有字符串字面值(如 "abc" )都作为此类的实例实现。

  • String是一个final类,代表不可变的字符序列。 字符串是常量,用双引号引起来表示。它们的值在创建之后不能更改。

  • String对象的字符内容是存储在一个字符数组value[]中的。

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
        /** The value is used for character storage. */
        private final char value[];
        /** Cache the hash code for the string */
        private int hash; // Default to 0

String的特性:

public static void main(String[] args) {

        String s1 = "Hello";
        String s2 = "World";
        String s3 = "Hello" + "World";
        String s4 = s1 + "World";
        String s5 = s1 + s2;
        String s6 = (s1 + s2).intern();

        System.out.println(s3 == s4);
        System.out.println(s3 == s5);
        System.out.println(s4 == s5);
        System.out.println(s3 == s6);
	//false
        //false
        //false
        //true
}

//结论:
// 常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量。 
// 只要其中有一个是变量,结果就在堆中
// 如果拼接的结果调用intern()方法,返回值就在常量池中

具体结论

String s1 = "a";

说明:在字符串常量池中创建了一个字面量为"a"的字符串。

s1 = s1 + "b";

说明:实际上原来的“a”字符串对象已经丢弃了,现在在堆空间中产生了一个字符

串s1+"b"(也就是"ab")。如果多次执行这些改变串内容的操作,会导致大量副本

字符串对象存留在内存中,降低效率。如果这样的操作放到循环中,会极大影响

程序的性能。

String s2 = "ab";

说明:直接在字符串常量池中创建一个字面量为"ab"的字符串。

String s3 = "a" + "b";

说明:s3指向字符串常量池中已经创建的"ab"的字符串。

String s4 = s1.intern();

说明:堆空间的s1对象在调用intern()之后,会将常量池中已经存在的"ab"字符串

赋值给s4。

1.2,String常用方法

int length():返回字符串的长度: return value.length
 char charAt(int index): 返回某索引处的字符return value[index]  boolean isEmpty():判断是否是空字符串:return value.length == 0 
 String toLowerCase():使用默认语言环境,将 String 中的所有字符转换为小写
 String toUpperCase():使用默认语言环境,将 String 中的所有字符转换为大写
 String trim():返回字符串的副本,忽略前导空白和尾部空白
 boolean equals(Object obj):比较字符串的内容是否相同
 boolean equalsIgnoreCase(String anotherString):与equals方法类似,忽略大小写
 String concat(String str):将指定字符串连接到此字符串的结尾。 等价于用“+”  int compareTo(String anotherString):比较两个字符串的大小
 String substring(int beginIndex):返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。 
 String substring(int beginIndex, int endIndex) :返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。
boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束
 boolean startsWith(String prefix):测试此字符串是否以指定的前缀开始
 boolean startsWith(String prefix, int toffset):测试此字符串从指定索引开始的子字符串是否以指定前缀开始
 boolean contains(CharSequence s):当且仅当此字符串包含指定的 char 值序列时,返回 trueint indexOf(String str):返回指定子字符串在此字符串中第一次出现处的索引
 int indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始
 int lastIndexOf(String str):返回指定子字符串在此字符串中最右边出现处的索引
 int lastIndexOf(String str, int fromIndex):返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索
注:indexOf和lastIndexOf方法如果未找到都是返回-1
 String replace(char oldChar, char newChar):返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。 
 String replace(CharSequence target, CharSequence replacement):使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。 
 String replaceAll(String regex, String replacement) : 使用给定的replacement 替换此字符串所有匹配给定的正则表达式的子字符串。 
 String replaceFirst(String regex, String replacement) : 使用给定的replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。 
 boolean matches(String regex):告知此字符串是否匹配给定的正则表达式。
 String[] split(String regex):根据给定正则表达式的匹配拆分此字符串。 
 String[] split(String regex, int limit):根据匹配给定的正则表达式来拆分此字符串,最多不超过limit个,如果超过了,剩下的全部都放到最后一个元素中。

2,StringBuffer

StringBuffer是在JDK1.0的时候诞生的,为线程安全的可变字符序列,主要解决String创建完字符串后不可变的问题。

2.1首先来看一下StringBuffer的变量

public final class StringBuffer extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence {
}

abstract class AbstractStringBuilder implements Appendable, CharSequence {
   /**
     * The value is used for character storage.
     */
    char[] value; //value没用final声明,可以不断进行扩容

   /**
     * The count is the number of characters used.
     */
    int count; //count用于记录有效字符的个数
}

此处的value就可以解释为什么StringBuffer为什么是可变的,因为他没有被final修饰,可以进行扩容,扩容机制详见下文。

2.2,StringBuffer构造器

StringBuffer不同于String,其对象必须使用构造器生成。有三个构造器:

 StringBuffer():初始容量为16的字符串缓冲区
 StringBuffer(int size):构造指定容量的字符串缓冲区
 StringBuffer(String str):将内容初始化为指定字符串内容

2.3,StringBuffer扩容机制

当没有指定初始化容量的时候,初始化容量为16,当超过16,容量会扩容,并且会创建一个新的数组,数组大小就是扩容后的容量; 机制:当前容量*2 + 2,请看下面代码

扩容实现:

public void ensureCapacity(int minimumCapacity) {
    if (minimumCapacity > 0)
        ensureCapacityInternal(minimumCapacity);
}

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
        //新的容量=当前的长度通过位运算符向右移动1位并且+2
        int newCapacity = (value.length << 1) + 2;
        //如果新的容量比当前容量小
        if (newCapacity - minCapacity < 0) {
            //新的容量=当前容量
            newCapacity = minCapacity;
        }
        //返回  如果新容量小于0 或者 新容量大于最大值 那么抛出异常 否则返回新容量
        return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
            ? hugeCapacity(minCapacity)
            : newCapacity;
}

首先扩容的方法是ensureCapacity方法,传入的参数是指定的容量,如果指定的容量>0,则调用ensureCapacityInternal方法,如果指定容量大与现有容量,之后调用newCapacity()`实现的。


3,StringBuilder

StringBuilder是JDK5的时候出现的,主要解决StringBuffer由于线程安全问题引起效率慢的问题。