日期:2014-05-20  浏览次数:20856 次

各位,下面的代码为什么?【对Java编译器优化的一点点认识】
Java code
  public static void main(String[] args) {
    String a = "abc";
    String b = "ab" + "c";
    System.out.println(a == b);// true?
    String c = "cd";
    String d = c + "ef";
    String e = "cdef";
    System.out.println(c == e);// false?
  }


------解决方案--------------------
String b = "ab" + "c"; 
这是常量运算,编译期就优化成"abc"了,和String b = "abc"是一样的结果。编译器找到有"abc"这个字符串,所以直接把其引用赋给b,而这个字符串原本已经赋了一个引用给a,所以a == b。

String d=c+"ef";
这个d是运行的时候才产生的,是由c字符串和"ef"通过连接运行得到的新的String对象,
String e="cdef"; 
e是编译期产生的,和b所引用的新的字符串对象不同。

其实要把这个事情搞清楚意义不大。比较字符串本来就应该使用equals方法而不是==。实际应用中也基本上不会用到==比较字符串(实际是比较引用)的情况。
------解决方案--------------------
好吧,这么说吧,Java是编译型语言。
无论他的语法分析是Yacc的生成,还是手工的。

都会解析到这样的语法。
String a = "abc";
String b = "ab" + "c"; 

那么它们会做什么呢? 解析到抽象语法树中,用伪码说,就是
a = "abc";
b = "ab" + "c"

注意,这个时候是编译中,而且处于前端,还没有到把语法树转化为字节码的阶段呢。
那么发现"ab" 和 "c"都是字符串常量。那么Yacc当然有能力把他变成"abc"了。

也就是说,当你看到了MyClass.class的时候,"abc"都已经生米煮成了熟饭了,是个板上钉钉的事情了。

而另外一个不一样。


d = c + "ef"
这个语法树不能进行,因为c是变量,不是常量或者是字面量。c在运行过程中,可能会被修改掉,比如这句之前,或者其他的线程。
我们这个语言,是上下文无关的问法。现在绝大多数的语言都是这类的。

那么既然都是"abc"了。
被JVM加载起来。那么就可以让String的实现类指向同一块 “内存地址”(这个理论上未必是真实的虚拟内存地址的,有些帖子说什么内存地址相等,我并不是很确定这个事情,我觉得要加个引号,如果说Ruby是内存相等,我信,源码很明显,就是Ruby语法给C语言加个壳子。)
所以结果是个true。

最后,这个事情不要太认真,绝对没有意义的Java问题。
a = "abc"
b = "abcd"
你说a==b ?
这个在java的虚拟机上是不等,但是我以为,修改了Java的String实现,就可以让它相等,只要再聚合一个offset成员就可以了。