今天有网友在群里面发了两道让他“感觉要命”的笔试题,大意如下
题目一
public class Interview{
public static void fn(String str){System.out.println("String");}
public static void fn(Object o){System.out.println("Object");}
public static void main(String[] args) {
fn(null);
}
}
请问结果是:
A.编译出错 B.运行出错 C.输出“String” D.输出 "Object"
这道题目可以说是老掉牙了,博主最初在某Java面试宝典上看到过的这道题目,后面在Java解惑中也看到过这道题目。显然这涉及了重载方法调用的选择的问题。JLS中关于方法重载的选择是这么说的:
The remainder of the process is split into three phases, to ensure compatibility with versions of the Java programming language prior to Java SE 5.0. The phases are:
- The first phase (§15.12.2.2) performs overload resolution without permitting boxing or unboxing conversion, or the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the second phase.
- The second phase (§15.12.2.3) performs overload resolution while allowing boxing and unboxing, but still precludes the use of variable arity method invocation. If no applicable method is found during this phase then processing
continues to the third phase. - The third phase (§15.12.2.4) allows overloading to be combined with variable arity methods, boxing, and unboxing.
If several applicable methods have been identified during one of the three phases of applicability testing, then the most specific one is chosen, as specified in section
翻译过来就是:
1.第一阶段,不允许装拆箱转换,或者变长参数,如果找不到可用的方法则执行第二阶段
2.的二阶段,执行允许装拆箱转换的重载解析,但是不允许变长参数,如果找不到可用的方法则执行第三阶段
3.第三阶段,允许重载与变长参数方法,装拆箱相结合
4.如果在任意一个阶段标识出了若干可运用的方法,则选择最具体的一个。
显然 null既可以转化为 Object 也可以转化为 String类型,确切地说“The null reference can always be assigned or cast to any reference type ”。但是String类型比Object更加具体,所以匹配String类型的方法。即选择 C
当然如果要是匹配的多个方法中一样的精确,那么则会在编译时报错
如:
public class Interview{
public static void fn(String str){
System.out.println("String");
}
public static void fn(Object o){
System.out.println("Object");
}
public static void fn(Integer i){
System.out.println("Integer");
}
public static void main(String[] args) {
fn(null);
}
}
这时,三个函数在第一阶段都可以匹配,String与Integer都比Object要更加精确匹配,但是String与Integer无法区分那个更加精确,编译器就无法判断调用那哪个方法了,因此编译出错。
题目二
public class CompoundAssignment{
public static void main(String[] args) {
int x = 10;
x += x -= x -= x;
System.out.println("x="+x);
}
}
输出结果为?
咋一看,结合顺序从右到左,所以应该是 x+= ( x-= ( x-=x ) ) 代入数值应该是 0 += (0 -= (10 -= 10)) 结果应该为 0
好吧,我们运行一下看看
结果:
x=20
什么?怎么是20?-_- 好吧,咱们又得翻一下JLS了。
在JLS中的Example 15.26.2-2.有这么一句话“Value Of Left-Hand Side Of Compound Assignment Is Saved Before Evaluation Of Right-Hand Side”,大意是“复合赋值左端的值是在右端计算之前保存的”,所以结果应该是 10 += (10 -= (10 -= 10)) 即 20
此外为了加深对这句话的理解,在此献上JLS中的例子
class Test{
public static void main(String[] args) {
int k = 1;
k += (k = 4) * (k + 2);
System.out.println("k = "+k);
}
}
您看结果是多少呢?
圉于博主的水平,理解可能有所偏差,还望各位大佬不吝指正!
参考:The Java Language Specification, Java SE 8 Edition