关于Java的一些编译机制
No.1 前几天在JavaEye上看到有人讨论Java对String常量优化的问题,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | package test; public class TestA { public static void main(String[] args) { String s="ab"; String s1="a"; String s2="b"; String s3=s1+s2; String s4="a"+"b"; System.out.println(s3==s4); System.out.println(s3=="ab"); System.out.println(s4=="ab"); } } |
结果:false
false
true
s3和s4虽然值相同,但是显然s3和s4并没有指向同一个String常量池中的String常量。String s4=”a”+”b”;,字符串连接的行为是在编译期间,编译器给优化了。而String s3=s1+s2;中的字符串连接是在代码运行期间进行的。这也间接的说明一个问题,就是String常量池在代码运行前就已经分配好了,代码运行时生成的String对象不会增加到String常量池中。因为s4指向的对象是在代码运行时新生成的对象,所以s3和s4没有指向同一个对象。
推荐这篇文章《Java编译器对于String常量表达式的优化》
No.2 Java编译涉及到的基本变量的问题。下面的代码
1 2 3 4 5 6 7 8 9 10 11 | package test; public class TestA { public static void main(String[] args) { short s=10;//这行代码可以通过编译 new TestA().go(11);//无法通过编译,需要进行类型转换 } short go(short s){ return 12;//可以通过编译 } } |
之前我一直无法理解为什么第5行和第9行可以通过编译,第6行不能通过编译,需要进行强制类型转换。因为这三行同样都是基本变量赋值的问题。后来我发现,这里面涉及到一个重载的问题。比如下面的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | package test; public class TestA { public static void main(String[] args) { short s=10; new TestA().go(11);//go方法会取调用最匹配的重载方法 } short go(short s){ return 12; } // short go(int i){ // return 12; // } short go(long l){ return 12; } } |
在go(int i)的方法注释掉之前,自然会调用go(int i)方法,注释掉之后,g(11)会调用最匹配的重载方法,而11既可以赋值给short型(强制类型转换),也可以赋值给long型(隐式类型转换),这是不可能的,不存在两个重载方法都匹配的情况,那样的话编译器无法知道真正要调用的方法到底是哪个。所以编译器无法为你主动做强制类型转换,,只能调用go(long l)方法。所以java规定第6行那样的情况需要做强制类型转换。