前言
为什么会写着篇博客,因为昨天看了关于final关键字的解析。但是有个问题始终没有得到解决,于是请教了我qq上之前添加的知乎大神。他给我回复的第一条消息:常量折叠。身为渣渣猿的我立马查询了这个概念。这是第一次知道这个概念。知乎大神还给我讲了好多。让我终于明白了这个常量折叠的概念
所谓常量折叠是java在编译期间做的一个优化,简单的来说就是在编译期就把一些表达式计算好,不需要在运行时进行计算。
下面话不多说了,来一起看看详细的介绍吧
实例解析
昨天,让我迷惑的代码是下面这段代码
1
2
3
4
5
6
7
8
9
|
public static void main(string[] args) {
string a = "hello2" ;
final string b = "hello" ;
string d = "hello" ;
string c = b + 2 ;
string e = d + 2 ;
system.out.println((a == c));
system.out.println((a == e));
}
|
这段的执行结果是
true
false
我就是不明白为什么第一个返回true呢?
留着这个疑问,我们先了解下常量折叠的概念。来更好的理解上面的代码
常量折叠
常量折叠的概念
- 常量折叠是一种编译器优化技术。
- 常量折叠主要指的是编译期常量加减乘除的运算过程会被折叠
对于 string s1 = "1" + "2";
编译器会给你优化成 string s1 = "12";
在生成的字节码中,根本看不到 "1" "2" 这两个东西。
我们通过idea进行验证下
1、源码文件
1
2
3
|
public static void main(string[] args) {
string s1 = "1" + "2" ;
}
|
2、运行后,idea有个out文件夹,找到上面文件的class文件
1
2
3
|
public static void main(string[] args) {
string s1 = "12" ;
}
|
确实如上面所说,编译器会给你进行优化
常量折叠发生的条件
必须是编译期常量之间进行运算才会进行常量折叠。
编译期常量就是“编译的时候就可以确定其值的常量”,
- 首先:字面量是编译期常量。(数字字面量,字符串字面量等)
- 其次:编译期常量进行简单运算的结果也是编译期常量,如1+2,"a"+"b"。
- 最后:被编译器常量赋值的 final 的基本类型和字符串变量也是编译期常量。
举个栗子
1.第一个栗子
1
2
3
4
5
|
public static void main(string[] args) {
string s1= "a" + "bc" ;
string s2= "ab" + "c" ;
system.out.println(s1 == s2);
}
|
相信大家都知道了,输出为true
并且只创建了一个 "abc" 字符串对象,且位于字符串常量池中。
2、第二个栗子
1
2
3
4
5
6
7
|
public static void main(string[] args) {
string a = "a" ;
string bc = "bc" ;
string s1 = "a" + "bc" ;
string s2 = a + bc;
system.out.println(s1 == s2);
}
|
这个结果呢?false
s1 是字符串字面量相加,但是 s2 却是两个非 final 的变量相加,所以不会进行常量折叠。
而是根据 string 类特有的 + 运算符重载,变成类似这样的代码
1
|
string s2 = new stringbuffer(a).append(b).tostring();
|
这里tostring()会生成新的string变量,显然用 == 运算符比较是会返回 false。
3、第三个栗子
1
2
3
4
5
6
7
|
public static void main(string[] args) {
final string a = "a" ;
final string bc = "bc" ;
string s1 = "a" + "bc" ;
string s2 = a + bc;
system.out.println(s1 == s2);
}
|
这里的结果就是true
因为 被编译器常量赋值的 final 的基本类型和字符串变量也是编译期常量
4、第四个栗子
1
2
3
4
5
6
7
8
|
public static void main(string[] args) {
string x = "a" ;
final string a = x;
final string bc = "bc" ;
string s1 = "a" + "bc" ;
string s2 = a + bc;
system.out.println(s1 == s2);
}
|
这里的结果是false
这里需要注意的是:final的变量,不是被编译期常量初始化的也不是编译器常量
这里的a 就不是编译器常量
总结
现在看完,是不是对上面打印的结果为什么是true 知道了呢?
所以。只要牢记常量折叠主要指的是编译期常量加减乘除的运算过程会被折叠
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对服务器之家的支持。
原文链接:https://www.cnblogs.com/zhenghengbin/p/9683990.html