String的运算 重载"+"与StringBuilder

参考《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机制

原文地址:https://www.cnblogs.com/wqff-biubiu/p/9167346.html