java之常量折叠

简介:

为什么会写着篇博客,因为昨天看了关于final关键字的解析。但是有个问题始终没有得到解决,于是请教了我qq上之前添加的知乎大神。他给我回复的第一条消息:常量折叠。身为渣渣猿的我立马查询了这个概念。这是第一次知道这个概念。知乎大神还给我讲了好多。让我终于明白了这个常量折叠的概念

实例解析

昨天,让我迷惑的代码是下面这段代码

    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、源码文件

    public static void main(String[] args) {
        String s1 = "1"+"2";
    }

2、运行后,idea有个out文件夹,找到上面文件的class文件

    public static void main(String[] args) {
        String s1 = "12";
    }

确实如上面所说,编译器会给你进行优化

常量折叠发生的条件

  • 必须是编译期常量之间进行运算才会进行常量折叠。
  • 编译期常量就是“编译的时候就可以确定其值的常量”

    • 首先:字面量是编译期常量。(数字字面量,字符串字面量等)
    • 其次:编译期常量进行简单运算的结果也是编译期常量,如1+2,"a"+"b"。
    • 最后:被编译器常量赋值的 final 的基本类型和字符串变量也是编译期常量。

举个栗子

1.第一个栗子

    public static void main(String[] args) {
        String s1="a"+"bc";
        String s2="ab"+"c";
        System.out.println(s1 == s2);
    }

相信大家都知道了,输出为true
并且只创建了一个 "abc" 字符串对象,且位于字符串常量池中。

2、第二个栗子

    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 类特有的 + 运算符重载,变成类似这样的代码 (jdk1.8)

String s2 = new StringBuilder(a).append(b).toString(); 

这里toString()会生成新的String变量,显然用 == 运算符比较是会返回 false。

3、第三个栗子

    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、第四个栗子

    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 就不是编译器常量

5、第五个栗子

    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.intern());
    }

这里的结果是true

其实,这里大家要明白intern这个方法的意思就会明白了

// 一个字符串池,最初是空的,是由类字符串私有维护的。
1、A pool of strings, initially empty, is maintained privately by the class String. 

// 如果常量池中已经有了这个字符串,那么直接返回常量池中它的引用,如果没有,那就将它的引用保存一份到字符串常量池,然后直接返回这个引用。
2、When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned. 

3、It follows that for any two strings s and t, s.intern() == t.intern() is true if and only if s.equals(t) is true.

总结

现在看完,是不是对上面打印的结果为什么是true 知道了呢?
所以。只要牢记常量折叠主要指的是编译期常量加减乘除的运算过程会被折叠

后续补充

上面一开始写的是

而是根据 String 类特有的 + 运算符重载,变成类似这样的代码

String s2 = new StringBuffer(a).append(b).toString(); 

这里更正下,就是现在我用jdk1.8 通过反编译后先在已经不是StringBuffer 而是StringBuilder。这里需要更正下。我也查询了下。StringBuilder 是jdk1.5之后才有的。

相关文章
|
10天前
|
Java 程序员 容器
Java中的变量和常量:数据的‘小盒子’和‘铁盒子’有啥不一样?
在Java中,变量是一个可以随时改变的数据容器,类似于一个可以反复打开的小盒子。定义变量时需指定数据类型和名称。例如:`int age = 25;` 表示定义一个整数类型的变量 `age`,初始值为25。 常量则是不可改变的数据容器,类似于一个锁死的铁盒子,定义时使用 `final` 关键字。例如:`final int MAX_SPEED = 120;` 表示定义一个名为 `MAX_SPEED` 的常量,值为120,且不能修改。 变量和常量的主要区别在于变量的数据可以随时修改,而常量的数据一旦确定就不能改变。常量主要用于防止意外修改、提高代码可读性和便于维护。
|
28天前
|
安全 Java
java BigDecimal 的赋值一个常量
在 Java 中,`BigDecimal` 是一个用于精确计算的类,特别适合处理需要高精度和小数点运算的场景。如果你需要给 `BigDecimal` 赋值一个常量,可以使用其静态方法 `valueOf` 或者直接通过字符串构造函数。 以下是几种常见的方法来给 `BigDecimal` 赋值一个常量: ### 使用 `BigDecimal.valueOf` 这是推荐的方式,因为它可以避免潜在的精度问题。 ```java import java.math.BigDecimal; public class BigDecimalExample { public static void
|
5月前
|
存储 小程序 Java
打破传统,Java 变量与常量让你重新认识编程的魅力!
【6月更文挑战第14天】Java编程中的变量与常量赋予程序灵活性和动态性。变量如魔法盒子,其值可变,提供程序的适应性;常量则如灯塔,值恒定,确保稳定性。两者结合构建复杂程序,变量用于处理数据,常量定义规则。在项目中,规范管理变量和常量能提升代码可读性和维护性。无论是小游戏还是复杂系统,它们都是不可或缺的工具,激发编程的无限可能。
27 1
|
2月前
|
Java
java小工具util系列4:基础工具代码(Msg、PageResult、Response、常量、枚举)
java小工具util系列4:基础工具代码(Msg、PageResult、Response、常量、枚举)
81 5
|
3月前
|
算法 Java 测试技术
Java零基础教学(15):Java常量详解
【8月更文挑战第15天】Java零基础教学篇,手把手实践教学!
63 5
|
3月前
|
存储 Java 测试技术
Java零基础(16) - Java常量
【8月更文挑战第16天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
43 4
|
5月前
|
存储 Java
Java 新手进阶:从变量到常量,一步步走向编程巅峰!
【6月更文挑战第14天】Java新手应掌握变量与常量,它们是编程基础。通过示例展示变量(如矩形的长度和宽度)用于存储可变数据,常量(如重力加速度)用于表示固定值。理解不同类型的变量,如字符串、整型和浮点型,并用`final`关键字定义常量。在银行账户管理程序案例中,变量跟踪账户信息,常量表示年利率。熟悉这些概念将提升编程技能。
29 3
|
5月前
|
存储 Java 容器
那些年,我们一起学过的 Java 变量与常量
【6月更文挑战第14天】Java学习中,变量和常量至关重要。变量如灵活容器,存储各类数据,示例:`int number = 10; number = 20;`。常量如圆周率`PI`,定义后不可变。Java有整型、浮点型、字符型、字符串型等变量和常量。合理使用能提升代码规范性和可读性,命名规范至关重要。变量和常量是编程基础,影响深远,共同构建稳定系统。
32 3
|
5月前
|
存储 Java
Java 变量与常量:深入浅出,一探究竟!
【6月更文挑战第14天】Java编程中的变量和常量是程序基础。变量是可变的数据存储,如`int num = 10;`,类型包括整型、浮点型等。常量(如`final double PI = 3.14159;`)值不可变。变量用于存储和处理数据,如计算平均分;常量常用于定义固定值,如颜色或π。理解和熟练使用变量与常量是编程的关键。
38 1
|
5月前
|
Java
Java常量总结
Java常量总结
下一篇
无影云桌面