java学习教程之常量折叠详解

前言

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

所谓常量折叠是Java在编译期间做的一个优化,简单的来说就是在编译期就把一些表达式计算好,不需要在运行时进行计算。

下面话不多说了,来一起看看详细的介绍吧

实例解析

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

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

String s2 = new StringBuffer(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 就不是编译器常量

总结

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

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • 浅谈java常量池

    java常量池技术 java中常量池技术说的通俗点就是java级别的缓存技术,方便快捷的创建一个对象.当需要一个对象时,从池中去获取(如果池中没有,就创建一个并放入池中),当下次需要相同变量的时候,不用重新创建,从而节省空间. java八种基本类型的包装类和对象池 java中的基本类型的包装类.其中Byte.Boolean.Short.Character.Integer.Long实现了常量池技术,(除了Boolean,都只对小于128的值才支持) 比如,Integer对象 Integer i1

  • java中变量和常量详解

    变量和常量 在程序中存在大量的数据来代表程序的状态,其中有些数据在程序的运行过程中值会发生改变,有些数据在程序运行过程中值不能发生改变,这些数据在程序中分别被叫做变量和常量. 在实际的程序中,可以根据数据在程序运行中是否发生改变,来选择应该是使用变量代表还是常量代表. 变量 变量代表程序的状态.程序通过改变变量的值来改变整个程序的状态,或者说得更大一些,也就是实现程序的功能逻辑. 为了方便的引用变量的值,在程序中需要为变量设定一个名称,这就是变量名.例如在2D游戏程序中,需要代表人物的位置,则需

  • Java中的常量避免反模式的方法

    在应用中,我们往往需要一个常量文件,用于存储被多个地方引用的共享常量.在设计应用时,我也遇到了类似的情况,很多地方都需要各种各样的常量. 我确定需要一个单独的文件来存储这些静态公共常量.但是我不是特别确定是应该用接口还是类(枚举不满足我的需求).我有两种选择: 使用接口,如: package one; public interface Constants { String NAME="name1"; int MAX_VAL=25; } 或 package two; public cla

  • Java关键字、标识符、常量、变量语法详解

    一.关键字 关键字:被Java语言赋予特定含义的单词.组成关键字的字母全部小写.注:goto和const作为保留字存在,目前并不使用.main并不是关键字. 二.标识符 标识符:就是给类,接口,方法,变量等起名字时使用的字符序列. 组成规则:英文大小写字母.数字字符.$和_ 注意事项:不能以数字开头.不能是Java中的关键字.区分大小写 我们通常会给下面的这些东西起一个标识符: 包.类或者接口.方法和变量.常量 三.注释 单行注释的格式: //注释文字 多行注释的格式: /* 注释文字 */ 文

  • 深入探索Java常量池

    Java的常量池通常分为两种:静态常量池和运行时常量池 静态常量池:class文件中的常量池,class文件中的常量池包括了字符串(数字)字面值,类和方法的信息,占用了class文件的大部分空间. 运行时常量池:JVM在完成加载类之后将class文件中常量池载入到内存中,并保存在方法区中.平时我们所讲的常量池就是指方法区中的运行时常量池.其相对于CLass文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量一定只有编译期才能产生,也就是并非预置入CLass文件中常量池的内容才能进入

  • Java 常量池的实例详解

    Java 常量池的实例详解 Java的常量池中包含了类.接口.方法.字符串等一系列常量值.常量池在编译期间就已经确定,并保存在*.class文件中 一.对于相同的常量值,常量池中只保存一份拷贝. 而且,当一个字符串由多个字符串常量链接而成时,多个字符串被组成一个字符串常量. 例如: package lxg; public class main { public static void main(String[] args) { String name = "lengxuegang";

  • java中定义常量方法介绍

    java没有叫全局变量的东西(有全局变量还叫OO吗?):原因:Java将所有设计都基于对象的基础上.Java的全局变量只能对一个类内部而言.能够在类内的任何地方使用的变量就是全局变量,而只在某个特定的函数或者局部的程序块内使用的变量则是局部变量. 所以:声明为static的变量实质上就是全局变量.当声明一个对象时,并不产生static变量的拷贝,而是该类所有的实例变量共用同一个static变量. 使用:java中只有static和非static变量,这个属于每个类的,如果需要全局变量比如PI(3

  • Java 常量与变量的区别详细介绍

       常量:其值不变即为常量. 语法: 数据类型 常量名 = 值; double PI = 3.14; 备注: 一般默认常量名大写. 变量与常量之间关系(量间关系) 先来一个简单的实例,好了解 Java 里变量与常量之间的关系. 下面的程序里声明了两种 Java 经常使用到的变量,分别为整型变量 num 与字符变量 ch.为它们赋值后,再把它们的值分别显示在控制台上: 下面的程序声明了两个变量,一个是整型,一个是字符型 public class TestJava{ public static v

  • Java 中的字符串常量池详解

    Java中的字符串常量池 Java中字符串对象创建有两种形式,一种为字面量形式,如String str = "droid";,另一种就是使用new这种标准的构造对象的方法,如String str = new String("droid");,这两种方式我们在代码编写时都经常使用,尤其是字面量的方式.然而这两种实现其实存在着一些性能和内存占用的差别.这一切都是源于JVM为了减少字符串对象的重复创建,其维护了一个特殊的内存,这段内存被成为字符串常量池或者字符串字面量池.

  • Java中的字符串常量池详细介绍

    Java中字符串对象创建有两种形式,一种为字面量形式,如String str = "droid";,另一种就是使用new这种标准的构造对象的方法,如String str = new String("droid");,这两种方式我们在代码编写时都经常使用,尤其是字面量的方式.然而这两种实现其实存在着一些性能和内存占用的差别.这一切都是源于JVM为了减少字符串对象的重复创建,其维护了一个特殊的内存,这段内存被成为字符串常量池或者字符串字面量池. 工作原理 当代码中出现字

随机推荐