`
zhxy0234
  • 浏览: 5599 次
  • 性别: Icon_minigender_1
  • 来自: 大连
最近访客 更多访客>>
社区版块
存档分类
最新评论

3.Java编译器对于String常量表达式的优化

阅读更多

首先把问题摆出来,先看这个代码


String a = "ab";
                        String b = "a" + "b";
                        System.out.println((a == b));



打印结果会是什么?类似这样的问题,有人考过我,我也拿来考过别人(蛮好玩的,大家也可以拿来问人玩),一般答案会是以下几种:


1.true

"a" + "b" 的结果就是"ab",这样a,b都是"ab"了,内容一样所以"相等",结果true

一般java新人如是答。

2.false

"a" + "a"会生成新的对象"aa",但是这个对象和String a = "ab";不同,(a == b)是比较对象引用,因此不相等,结果false

对java的String有一定了解的通常这样回答。

3.true

String a = "ab";创建了新的对象"ab"; 再执行String b = "a" + "b";结果b="ab",这里没有创建新的对象,而是从JVM字符串常量池中获取之前已经存在的"ab"对象。因此a,b具有对同一个string对象的引用,两个引用相等,结果true.

能回答出这个答案的,基本已经是高手了,对java中的string机制比较了解。

很遗憾,这个答案,是不够准确的。或者说,根本没有运行时计算b = "a" + "b";这个操作.实际上运行时只有String b = "ab";

3的观点适合解释以下情况:


String a = "ab";
                        String b = "ab";
                        System.out.println((a == b));


如果String b = "a" + "b";是在运行期执行,则3的观点是无法解释的。运行期的两个string相加,会产生新的对象的。(本文后面对此有解释)


4.true

下面是我的回答:编译优化+ 3的处理方式 = 最后的true

String b = "a" + "b";编译器将这个"a" + "b"作为常量表达式,在编译时进行优化,直接取结果"ab",这样这个问题退化


String a = "ab";
                        String b = "ab";
                        System.out.println((a == b));



然后根据3的解释,得到结果true


这里有一个疑问就是String不是基本类型,像

int secondsOfDay = 24 * 60 * 60;

这样的表达式是常量表达式,编译器在编译时直接计算容易理解,而"a" + "b" 这样的表达式,string是对象不是基本类型,编译器会把它当成常量表达式来优化吗?

下面简单证明我的推断,首先编译这个类:


public class Test {
                        private String a = "aa";
                        }


复制class文件备用,然后修改为


public class Test {
                        private String a = "a" + "a";
                        }


再次编译,用ue之类的文本编辑器打开,察看二进制内容,可以发现,两个class文件完全一致,连一个字节都不差.

ok,真相大白了.根本不存在运行期的处理String b = "a" + "b";这样的代码的问题,编译时就直接优化掉了。


下面进一步探讨,什么样的string + 表达式会被编译器当成常量表达式?

String b = "a" + "b";

这个String + String被正式是ok的,那么string + 基本类型呢?


String a = "a1";
                        String b = "a" + 1;
                        System.out.println((a == b)); //result = true
                        String a = "atrue";
                        String b = "a" + true;
                        System.out.println((a == b)); //result = true
                        String a = "a3.4";
                        String b = "a" + 3.4;
                        System.out.println((a == b)); //result = true



可见编译器对string + 基本类型是当成常量表达式直接求值来优化的。


再注意看这里的string都是"**"这样的,我们换成变量来试试:


String a = "ab";
                        String bb = "b";
                        String b = "a" + bb;
                        System.out.println((a == b)); //result = false



这个好理解,"a" + bb中的bb是变量,不能进行优化。这里很很好的解释了为什么3的观点不正确,如果String+String的操作是在运行时进行的,则会产生新的对象,而不是直接从jvm的string池中获取。


再修改一下,把bb作为常量变量:


String a = "ab";
                        final String bb = "b";
                        String b = "a" + bb;
                        System.out.println((a == b)); //result = true


竟然又是true,编译器的优化好厉害啊,呵呵,考虑下面这种情况:


String a = "ab";
                        final String bb = getBB();
                        String b = "a" + bb;
                        System.out.println((a == b)); //result = false
                        private static String getBB() {
                        return "b";
                        }


看来java(包括编译器和jvm)对string的优化,真的是到了极点了,string这个所谓的"对象",完全不可以看成一般的对象,java对string的处理近乎于基本类型,最大限度的优化了几乎能优化的地方。


另外感叹一下,string的+号处理,算是java语言里面唯一的一个"运算符重载"(接触过c++的人对这个不会陌生)吧?
分享到:
评论

相关推荐

    2Java SE(上).doc

    java编译器有一个优化措施,就是若计算表达式运算符两边都是字面量,那么编译器在生成class文件时就将结果计算完毕并保存到编译后的class文件中了。 3. String使用了final修饰,不能被继承 方法: 1)int length...

    Java面试宝典(传说中的葵花宝典).doc

    由于 += 是java语言规定的运算符,java编译器会对它进行特殊处理,因此可以正确编译。 7、char型变量中能不能存贮一个中文汉字?为什么? char型变量是用来存储Unicode编码的字符的,unicode编码字符集中包含了汉字...

    JAVA复习资料

    7、设x=2.5,a=7,y=4.7,算术表达式x+a%3*(int)(x+y)%2/4的值为:2.75___ 8、被关键字_final___修饰的方法是不能被当前类的子类重新定义的方法。 9、Java中类成员的限定词有以下几种:private, _protected__, ...

    PHP官方手册中文版

    13. 常量 14. 表达式 15. 运算符 16. 控制结构 17. 函数 18. 类与对象(PHP 4) 19. 类与对象(PHP 5) 20. Namespaces 21. 异常处理 22. 引用的解释 IV. 安全 23. 简介 24. 总则 25. 以 CGI 模式安装...

    PHP手册2007整合中文版

    13. 常量 14. 表达式 15. 运算符 16. 控制结构 17. 函数 18. 类与对象(PHP 4) 19. 类与对象(PHP 5) 20. Namespaces 21. 异常处理 22. 引用的解释 IV. 安全 23. 简介 24. 总则 25. 以 CGI 模式安装时 26. 以 ...

    Java 语言基础 —— 非常符合中国人习惯的Java基础教程手册

    而对于对象所在的实际的内存地址是不可操作的,这就保证了安全性。 1.8.4 对象的引用 对象的使用包括引用对象的成员变量和方法,通过运算符·可以实现对变量的访问和方法的调 用,变量和方法可以通过设定一定的...

    java 面试题 总结

    java编译器要求方法必须声明抛出可能发生的非运行时异常,但是并不要求必须声明抛出未被捕获的运行时异常。 6、说出Servlet的生命周期,并说出Servlet和CGI的区别。 Servlet被服务器实例化后,容器运行其init方法,...

    超级有影响力霸气的Java面试题大全文档

    java编译器要求方法必须声明抛出可能发生的非运行时异常,但是并不要求必须声明抛出未被捕获的运行时异常。 9、说出Servlet的生命周期,并说出Servlet和CGI的区别。  Servlet被服务器实例化后,容器运行其init方法...

    【05-面向对象(下)】

    parseXxx(String s)静态方法。 •如果将基本类型转换为这符串,只需在后面加+ “”进行连接运算。 Java 7对包装类的增强 •Java 7为所有包装类增加一个新方法: compare(x , y)的方法。该方法用于比较两...

    C#微软培训资料

    7.4 关系操作符和关系表达式.65 <<page 2>> page begin==================== 7.5 逻辑操作符和逻辑表达式.68 7.6 位 运 算 .69 7.7 其它特殊操作符 .72 7.8 小 结 .77 第八章 流 程 控 制 .79 8.1 ...

    PHP5 完整官方 中文教程

    常量 表达式 运算符 控制结构 函数 类与对象(PHP 4) 类与对象(PHP 5) Namespaces 异常处理 引用的解释 安全 简介 总则 以 CGI 模式安装时 以 Apache 模块安装时 文件系统安全 数据库安全 错误报告 使用 Register...

    PHP5中文参考手册

    常量 表达式 运算符 控制结构 函数 类与对象(PHP 4) 类与对象(PHP 5) Namespaces 异常处理 引用的解释 安全 简介 总则 以 CGI 模式安装时 以 Apache 模块安装时 文件系统安全 数据库安全 错误报告 使用 Register...

    C++大学教程,一本适合初学者的入门教材(part2)

    20.5.3 remove、remove_if、 remove_copy和remove_copy_if 20.5.4 replace、replace_if、replace_copy和replace_copy_if 20.5.5 数学算法 20.5.6 基本查找与排序算法 20.5.7 swap、iter_swap和swap_ranges...

    C++大学教程,一本适合初学者的入门教材(part1)

    20.5.3 remove、remove_if、 remove_copy和remove_copy_if 20.5.4 replace、replace_if、replace_copy和replace_copy_if 20.5.5 数学算法 20.5.6 基本查找与排序算法 20.5.7 swap、iter_swap和swap_ranges...

Global site tag (gtag.js) - Google Analytics