常量折叠是Java在编译期间做的一个优化,简单的来说就是在编译期就把一些表达式计算好,不需要在运行时进行计算。
对于如下代码:
String s1 = "a" + "b";
int a = 1 + 3;
java编译器在编译阶段会把值计算出来,也就会变成:
String s1 = "ab";
int a = 4;
对于如下代码(使用javac编译)我们使用Luyten或者JD-GUI打开,可以看到经过优化后的代码:
public class Test {
public static void main(String[] args) {
String s1 = "a" + "b";
String s2 = "a";
String s3 = s2 + "b";
System.out.println(s1 == "ab");
System.out.println(s3 == "ab");
}
}
所以输出为:
true
false
解释:
String str1 = "ab";
String str2 = "ab";
因为String是不可变的,所以没有必要弄出两个”ab”对象,在内存中(字符串常量池中)只有一个指向”ab”的引用,str1和str2都指向它,所以这里str1==str2应该不难理解。(实际上“ab”还是存在于堆中)
故System.out.println(s1 == "ab");
输出为 true
对于 String s3 = s2 + "b";
内部实现实际上是创建了一个StringBuilder,然后一直append,最后在toString。
这就是java在String中如何对“+”与“+=”进行运算符重载的实现。
(“+”和“+=”是java中仅有的两个运算符重载。)
String s3 = new StringBuilder().append(“a”).append(“b”).toString();
@Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
它new了一个对象,new出来的东西位于堆上,也就是说s3指向了堆上的一块内存,而s1指向的东东位于常量池,所以使用==会返回false。
进行常量折叠需要满足:
1. 编译时常量(数字字面值,字符串字面值等)。
2. 编译时常量进行简单运算的结果也是编译时常量,如1 + 2,”a”+ “b”。
3. 被 final 修饰的基本类型和字符串变量也是编译时常量。
如:
public class Main {
static final String a = "a";
static final String b = "b";
public static void main(String[] args) {
String s1= a + b;
String s2= a + b;
System.out.println(s1 == s2);
}
}
输出也为 true。