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方法。