Java String总结

Java String总结

String 类

String类定代码如下:

/**
* The String class represents character strings. All
* string literals in Java programs, such as "abc", are
* implemented as instances of this class.
* 

* Strings are constant; their values cannot be changed after they * are created. String buffers support mutable strings. * Because String objects are immutable they can be shared. */ public final class String implements java.io.Serializable, Comparable, CharSequence { }

从代码看有两点需要注意:

  1. String类为 final 不可以被继承
  2. String对象一旦创建就不可改变

常量池

常量池常量池指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。包含代码中所定义的各种基本类型(如int、long等等)和对象型(如String及数组)的常量值(final),以及还包含一些以文本形式出现的符号引用,比如:

  • 类和接口的全限定名
  • 字段的名称和描述符
  • 方法和名称和描述符

以字面值出现的字符串会保存在常量池中,以new创建的字符串则会保存在堆中,例如:

String str = "ABC";

这段代码执行时,首现创建一个String类型的引用str,然后再常量池中,查找"ABC",如果找到则str指向"ABC",如果没有找到先将"ABC"放入常量池,再将str指向"ABC"。下面是一个例子:

public class ContactPollTest {

    String mString = "ABC";

    /**
     * @param args
     */
    public static void main(String[] args) {
        ContactPollTest newTest = new ContactPollTest();
        newTest.testConstantPoll();
    }

    public void testConstantPoll() {
        String stra = "ABC";
        String strb = "ABC";
        String strc = "A" + "BC";
        String strd = "A";
        String stre = strd + "BC";
        final String strf = "A";
        String strg = strf + "BC";
        String strh = new String("ABC");
        System.out.print("testFieldAndVariant stra = mString?:" + (stra == mString) + "\r\n");
        System.out.print("testConstantPoll stra = strb?:" + (stra == strb) + "\r\n");
        System.out.print("testConstantPoll stra = strc?:" + (stra == strc) + "\r\n");
        System.out.print("testConstantPoll stra = stre?:" + (stra == stre) + "\r\n");
        System.out.print("testConstantPoll stra = strg?:" + (stra == strg) + "\r\n");
        System.out.print("testConstantPoll stra = strh?:" + (stra == strh) + "\r\n");
        System.out.print("testConstantPoll stra = stri?:" + (stra == stri) + "\r\n");
        System.out.print("testConstantPoll stra = strj?:" + (stra == strj) + "\r\n");
    }

    static String getString() {
        return "BC";
    }
    
    final static String sFianlString = "BC";
    
    static String getFinalString() {
        return sFianlString;
    }
}

输出结果如下:

testFieldAndVariant stra = mString?:true
testConstantPoll stra = strb?:true
testConstantPoll stra = strc?:true
testConstantPoll stra = stre?:false
testConstantPoll stra = strg?:true
testConstantPoll stra = strh?:false
testConstantPoll stra = stri?:false#

解释如下:

  1. mString 和 stra,strb都指向了常量池中的 "ABC" 因此前行输出为true
  2. String strc = "A" + "BC" 在编译时被优化为了 String stra = "ABC"因此第三行输出为true
  3. String stre = strd + "BC",引用了strd,需要重新在堆中创建
  4. strf为final,因此strg = strf + "BC"; 也被编译器优化为strg = "ABC";
  5. strh是通过new创建,所以内存分配在堆上,因此最后一行结果为false
  6. 通过函数获得字符串,都会在堆上创建新的对象

StringBuilder & StringBuffer

String具有不可变性,StringBuilderStringBuffer用于创建一个可变的字符串,两者的区别在后者是前者的同步版本。下面我们来比较几段代码的性能:

public class StringBuilderTest {

    /**
     * @param args
     */
    public static void main(String[] args) {
        testStringConstant();
        testStringVariant();
        testStringBuilder();
        testStringBuffer();
    }

    static void testStringConstant() {
        String a = "";
        long begin = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            a += "a";
        }
        long end = System.currentTimeMillis();
        System.out.print("testStringConstant consume:" + (end - begin) + " \r\n");
    }

    static void testStringVariant() {
        String b = "a";
        String a = "";
        long begin = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            a += b;
        }
        long end = System.currentTimeMillis();
        System.out.print("testStringVariant consume:" + (end - begin) + " \r\n");
    }

    static void testStringBuilder() {
        StringBuilder sb = new StringBuilder();
        long begin = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            sb.append("a");
        }
        long end = System.currentTimeMillis();
        System.out.print("testStringBuilder consume:" + (end - begin) + " \r\n");
    }

    static void testStringBuffer() {
        StringBuffer sb = new StringBuffer();
        long begin = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            sb.append("a");
        }
        long end = System.currentTimeMillis();
        System.out.print("testStringBuffer consume:" + (end - begin) + " \r\n");
    }

}

输出结果为:

testStringConstant consume:3902
testStringVariant consume:3538
testStringBuilder consume:6
testStringBuffer consume:6

性能的差异可以从编译出的字节码看出:

  • testStringConstant
    [站外图片上传中...(image-e51349-1511918381002)]

从字节码的9行可以看出编译器已经使用StringBuilder进行优化,但是每次循环都会new一个StringBuilder对象。

  • testStringVariant


    Java String总结_第1张图片
    testStringVariant

    从字节码的10行可以看出编译器已经使用StringBuilder进行优化,但是每次循环都会new一个StringBuilder对象.
    和testStringConstant的性能差距应该在于前者"a"需要在常量池中查找,后者直接通过栈中的引用取值。

  • testStringBuilder & testStringBuffer


    Java String总结_第2张图片
    testStringBuilder

    从字节码可以看出testStringBuilder函数的写法,可以避免多次创建StringBuilder对象,以及该toString方法。testStringBuffer的情况类似。

你可能感兴趣的:(Java String总结)