参考《think in Java》第四版 第十三章 13.2 重载“+”与StringBuilder
上原书代码:
public class Concatenation{ public static void main(String args[]){ String mango = "mango"; String str = "abc" + mango + "deg" + 47; System.out.println(str); } }
.class文件 反编译
javac Concatenation.java
javap -c Concatenation
结论:等同于new StringBuilder().append("abc").append(mango).append("deg").append(47).toString();
思考:常见的一个问题String Str = "a" + "b" + "c";生成了几个String对象,或者生成了几个对象。
看《think in java》的例子很容易进入误区:new StringBuilder.append("a").append("b").append("c");下面看个例子:
public class StringTest{ public static void main(String args[]){ String str = "a" + "b" + "c"; String str2 = str + "d" + "f";
String str3 = str + "d" + "f" + 47 + "g"; } }
可以看出 String str = "a" + "b" = “c”; 在编译期直接编译成String str = "abc";
后面String Str2 = str + "d" + "f";在编译期编译成String str2 = new StringBuilder().append("abc").append("df").toString();
后面String Str3 = str + "d" + "f" + 47 +"g";在编译期编译成String str2 = new StringBuilder().append("abc").append("d").append(47).append("g").toString();
为什么
java编译期有应用到“合并已知量”的优化技术。
这里我去找了下编译原理关于代码优化相关的知识
代码优化的分类:
根据设计程序范围分类:全局优化,局部优化,循环优化
根据技术的分类:
删除多余运算:对于相同的子表达式,在第一次出现时进行计算,且值计算一次,其结果带入ti,以后重复的地方直接带入ti,不重复运算,节约时间及空间
强度削弱:在不改变运算结果的前提下,将程序中执行时间长的运算替换成执行时间短的运算。x^3可用x*x*x实现
合并已知量:若参加运算的两个量都是已知量,则在编译时直接计算出结果。1+2编译期直接3
复写传播:尽量不引用那些程序中只传递信息而不改变值,不影响运行结果的变量。
循环优化:将循环中的不变量提到循环外面。
所以String str = "a' +“b” +"c";合并已知量为String str = "abc";
其实《think in java》中在提到final关键字时有一段话,表明java编译时应用了此技术:
一个永不改变的编译期常量:对于编译期常量这种情况,编译期可以将该常量值带入到任何可能运用到的计算式中,也就是说可以在编译时执行计算式,减轻运行时的负担,但这类常量必须是基本数据类型,且必须final修饰初始化。
所以综上,不结合上下文String Str2 = str + "d" + "f"中str是未知的。编译时优化为StringBuilder,有趣的是后面的“d”+"f"合并了,然后str3中我们看到了并没有合并为"df47e"
String str = "a" + "b" + "c";编译为String str = "abc"; 建立了一个String对象“abc”。
另外查找资料的时候发现一个名词StringPool(字符串常量池)
由于java对String操作频繁,所以对String类做了很多优化,StringPool就是其中之一,StringPool是运行期维护于常量池中,处于GC永久代。
创建String对象的两种方式
String str = new String("aa"); String str = "aa";
"aa"会存入StringPool,当下次使用时“aa”,String会先到StringPool查找“aa”,找到了把地址赋给引用对象,也就是指向同一对象(内存),没找到建立新对象。
String str = "a"; String str2 = "a"; System.out.println(str == str2);//true
当然无论何时,new 一个对象都会创建一个新的对象。
拓展;Integer类的cache机制