为什么会写着篇博客,因为昨天看了关于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之后才有的。

java之常量折叠的更多相关文章

  1. C++中的常量折叠

    先看例子: #include <iostream> using namespace std; int main() { ; int * p = (int *)(&a); *p = ...

  2. 常量折叠 const folding

    http://bbs.byr.cn/#!article/CPP/86336?p=1 下列代码给出输出结果: #include"stdafx.h" #include <iost ...

  3. eclipse中,把java函数代码折叠/展开

    首先,在eclipse 中开启设置代码折叠功能 1. windows->perferences->General->Editors->Structured Text Edito ...

  4. const常量折叠

    首先来看一个例子: int main(int argc, char* argv[]) { ; int *j = (int *) &i; *j=; cout<<&i<& ...

  5. C++的常量折叠(二)

    前面的C++的常量折叠(一)的最后留下了一个问题,那就是在声明i的时候,加上修饰符volatile关键字,发现结果输出的就不一样了,下面来说一下volatile这个关键字. C/C++中的volati ...

  6. C++的常量折叠(一)

    前言 前几天女票问了我一个阿里的面试题,是有关C++语言的const常量的,其实她一提出来我就知道考察的点了:肯定是const常量的内存不是分配在read-only的存储区的,const常量的内存分配 ...

  7. -1-1 java 基础语法 java关键字 java 注释 常量 语句 运算符 函数 数组定义

    Java语言基础组成 关键字 标识符 注释 常量和变量 运算符 语句 函数 数组 关键字 定义:被Java语言赋予了特殊含义的单词 特点:关键字中所有字母都为小写 用于定义数据类型的关键字 class ...

  8. java中常量文件的配置与读取

    java中常量文件的配置与读取: package com.floor.shop.user.util; import java.io.InputStream; import java.io.InputS ...

  9. JAVA编译器常量

    编译器常量的特点就是:它的值在编译期就可以确定.比如: final int i = 5; 再傻的编译器也能在编译时看出它的值是5,不需要到运行时.对于运行时常量,它的值虽然在运行时初始化后不再发生变化 ...

随机推荐

  1. 【转】mysql增量备份恢复实战企业案例

    来源地址:http://seanlook.com/2014/12/05/mysql_incremental_backup_example/ 小量的数据库可以每天进行完整备份,因为这也用不了多少时间,但 ...

  2. input输入框的光标定位的问题

    input输入框的光标定位的问题 在给input输入框赋值的时候,或者在我之前写模拟下拉框js组件的时候,时不时会碰到光标的小bug问题,比如键盘中的 上移或者下移操作,在浏览器中光标会先移到最前端, ...

  3. 前端:background 设置

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. WebView之加载网页时增加进度提示

    上一节讲了一些webview的基本使用以及在记载网页时如何屏蔽掉第三方浏览器,使我们自己开发的程序成为一个微型浏览器.那么这一节将一下在webView加载网页的过程中如何加上进度提示.效果图如下: 主 ...

  5. TTL,COMS,USB,232,422,485电平之详细介绍及使用

    如有错误敬请指导! 今天来详细介绍一下TTL,COMS,USB,232,422,485电平,以及之间的转换问题. 有些地方的引脚图可能不是规范的,具体引脚以自己的模块资料为主,这篇文章着重介绍使用.. ...

  6. STM32学习之路入门篇之指令集及cortex——m3的存储系统

    STM32学习之路入门篇之指令集及cortex——m3的存储系统 一.汇编语言基础 一).汇编语言:基本语法 1.汇编指令最典型的书写模式: 标号 操作码        操作数1, 操作数2,... ...

  7. AbelSu玩Kotlin

    Kotlin是一门基于JVM的编程语言,它正成长为Android开发中用于替代Java语言的继承者. Kotlin是由JetBrains创建的基于JVM的编程语言,IntelliJ正是JetBrain ...

  8. 秋风下的萧瑟 NOIP2018 游记

    “北方的秋天还真的是美丽冻人呢!” 是么?我有些疑惑,任凭雨滴落在脸上. 这天,可真不好,秋雨可让这天气一天比一天的寒冷了. 大概,故事从这里开始吧? 上一次的故事说道了哪里?那么,我们从今天的新故事 ...

  9. 设计模式学习---UML常见关系的实现

    一.UML基本构造 UML的基本构造含3种: (1) 事物(4种):结构事物,行为事物,分组事物,注释事物 (2) 关系(4种):泛化关系,实现关系,依赖关系,关联关系 (3) 图(10种):用例图, ...

  10. 洛咕 P3704 [SDOI2017]数字表格

    大力推式子 现根据套路枚举\(\gcd(i,j)\) \(ans=\Pi_{x=1}^nfib[x]^{\sum_{i=1}^{n/x}\sum_{j=1}^{n/x}[\gcd(i,j)=1]}\) ...