以下内容为thinking in java(p504-p509)总结:
java中没有运算符重载,但实际上“+”在java中实现了重载,一条语句中第一个”+“当且运算的一方为String对象时,是将另一方转化为字符串并将两个字符串拼接在一起,其实现是在编译时如果发现一条语句中第一个“+”一方为String对象,则创建一个stringbuilder对象,利用其append方法 ,最后toString转换为字符串返回。可以使用以下代码和javap命令验证。
javap是jdk自带的工具,实现反汇编,即将字节码文件转换为汇编语言。以下代码:
public class TestString{
public String concat() {
String str = "";
return str+"hello"+"world"+"java!"+this;
}
public static String concatenation (String str) {
for (int i=0;i<6;i++) {
str+=i+",";
}
return str;
}
public static String concatenation (StringBuilder sb) {
for (int i=0;i<6;i++) {
sb.append(i+",");
//sb.append(",");
}
return sb.toString();
}
public static void main(String[] args) {
String str = "";
StringBuilder sb = new StringBuilder();
concatenation(str);
concatenation(sb);
}
}
先将该java文件编译,再使用javap -c命令查看反编译代码,第一个方法反编译结果为:
public java.lang.String concat();
Code:
0: ldc #2; //String
2: astore_1
3: new #3; //class java/lang/StringBuilder
6: dup
7: invokespecial #4; //Method java/lang/StringBuilder.””:()V
10: aload_1
11: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
14: ldc #6; //String hello
16: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: ldc #7; //String world
21: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: ldc #8; //String java!
26: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
29: aload_0
30: invokevirtual #9; //Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
33: invokevirtual #10; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
36: areturn
发现第3:创建stringBuilder对象, 第7:初始化, 第11:开始调用append方法,添加对象,最后调用自身toString方法并返回。
我们发现一条语句中“+”一方第一次出现字符串对象,则会创建stringBuilder对象,那么编译器自动创建并转换和手动编码并转换有什么区别呢?以两个循环方法为例:
public static java.lang.String concatenation(java.lang.String);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: bipush 6
5: if_icmpge 38
8: new #3; //class java/lang/StringBuilder
11: dup
12: invokespecial #4; //Method java/lang/StringBuilder.””:()V
15: aload_0
16: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: iload_1
20: invokevirtual #11; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
23: ldc #12; //String ,
25: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: invokevirtual #10; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
31: astore_0
32: iinc 1, 1
35: goto 2
38: aload_0
39: areturn
第5:进入循环,每次循环创建一个StringBuilder对象,初始化后append添加,并自动toString,将堆内存首地址值赋给str,最后返回。
而手动编码并转换的过程如下:
public static java.lang.String concatenation(java.lang.StringBuilder);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: bipush 6
5: if_icmpge 27
8: aload_0
9: iload_1
10: invokevirtual #11; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
13: pop
14: aload_0
15: ldc #12; //String ,
17: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
20: pop
21: iinc 1, 1
24: goto 2
27: aload_0
28: invokevirtual #10; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
31: areturn
每次循环,直接调用stringBuilder对象append方法,最后显示调用toString并返回。相对于手动使用stringBuilder来说,前者占用内存空间且效率低。要注意的是,当在手动使用stringBuilder时如果使用”+“连接字符串则会创建新的stringBuilder对象。
然后,再讲一下String的intern方法,当String对象调用intern方法时,如果该对象与字符串常量池对象比较,存在相等的情况(根据equals方法返回值),则将常量池中该对象返回,否则,该对象加入到常量池中,并返回该对象的引用。intern方法的作用主要在于节约内存。