6、StringTable

先看下面代码

public class StringNewTest {
    public static void main(String[] args) {
        String str = new String("a")+new String("b");
    }
}

上面代码创建了几个对象?

我们来看一下字节码

Code:
      stack=4, locals=2, args_size=1
         0: new           #2                  // class java/lang/StringBuilder
         3: dup
         4: invokespecial #3                  // Method java/lang/StringBuilder."<init>":()V
         7: new           #4                  // class java/lang/String
        10: dup
        11: ldc           #5                  // String a
        13: invokespecial #6                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
        16: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        19: new           #4                  // class java/lang/String
        22: dup
        23: ldc           #8                  // String b
        25: invokespecial #6                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
        28: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        31: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        34: astore_1
        35: return
对象1:  0:new 创建了一个StringBuilder对象
对象2:  7:new 创建了一个String对象
对象3:  11:ldc 字符串常量池中“a”对象
对象4:  19:new String对象
对象5:  23:ldc  字符串常量池中“b”对象
深入了解
		31:invokevirtual 调用StringBuilder的toString方法
@Override
public String toString() {
    // Create a copy, don't share the array
    return new String(value, 0, count);
}		
可以看到又创建了一个String对象。此时,字符串常量池中没有“ab”
所以一共6个对象
public class StringIntern {
    public static void main(String[] args) {
        String s = new String( "1" );
        s.intern();
        String s2 = "1";
        System.out.println( s == s2 );

        String s3 = new String( "1" ) + new String( "1" );
        s3.intern();
        String s4 = "11";
        System.out.println( s3 == s4 );
    }
}

String s = new String("1"),可以知道在堆空间创建了一个String对象,字符串常量池中有个“1”对象,调用s.intern(),发现字符串常量池中有一个“1”了,s2指向的是字符串常量池中的“1”,s和s2不是一个内存地址,所以s==s2结果为false。

s3和s4,由上面的可以知道,s3最后接收的是StringBuilder.toString()方法,会new Stirng(),所以s3指向堆空间的地址,而且字符串常量池中也没有“11”,调用s3.intern(),会在字符串常量池中添加一个“11”,为了节省空间,字符串常量池中的这个对象会指向堆空间中的“11”。s4指向字符串常量池中的对象,最终也会指向堆中的“11”,所以最终地址也是一样的,所以s3==s4结果为true。(JK8,为true,是因为字符串常量池在堆中。jdk6结果为false,是因为字符串常量池在永久代,调用intern会重新开辟一块空间,不会指向堆中的“11”,所以为false。)

Intern的空间效率测试

public class StringIntern2 {
    private static final int MAX_COUNT = 1000 * 10000;
    static final String[] arr = new String[MAX_COUNT];

    public static void main(String[] args) {
        Integer[] data = new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        long start = System.currentTimeMillis();
        for (int i = 0; i < MAX_COUNT; i++) {
            // arr[i] = new String( String.valueOf( data[i % data.length] ) );
            arr[i] = new String( String.valueOf( data[i % data.length] ) ).intern();
        }
        long end = System.currentTimeMillis();
        System.out.println( "花费的时间为:" + (end - start) );
        try {
            Thread.sleep( 1000000 );
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.gc();
    }
}

使用intern的

没有使用intern

可以看到使用intern的,内存占用要小得多,而且执行时间也少得多,所以在有大量重复字符串的操作时,可以考虑使用intern方法。

posted @ 2021-10-26 20:13  程序员清风  阅读(28)  评论(0编辑  收藏  举报