JAVA方法重载
下面我们来一一介绍这些内容
方法返回值不能作为重载的依据
方法名相同但是方法的形式参数不同的一些方法,称这些方法为重载的方法。形式参数不同可以分为参数的类型不同,和参数的顺序不同。方法名和方法的形式参数我们称为方法的签名。区分方法的重载靠的是方法的签名,而与方法的返回值没有关系。下面我来解释下为什么方法的返回值不能作为方法重载区分的条件。有如下两个方法:
void f(){}
int f(){}
只要编译器可以根据语境明确判断出语义,比如在int x = f()中,那么的确可以据此区分出重载的方法。不过,有的时候你不关心返回值,比如下面这样:
f() ;
此时,java就不知道该调用哪个方法了。所以根据方法的返回值来区分方法重载是行不通的。
静态类型判断使用哪个方法重载
下面带给大家看一道经常用作java面试题来解释方法重载的过程。
public class StaticDispatch {
static abstract class Human{}
static class Man extends Human{}
static class Woman extends Human{}
public void sayHello(Human guy){ //1
System.out.println("hello guy");
}
public void sayHello(Man guy){ //2
System.out.println("Hello ,Man");
}
public void sayHello(Woman guy){ //3
System.out.println("Hello ,Woman");
}
public static void main(String[] args) {
Human man = new Man() ;
Human woman = new Woman() ;
StaticDispatch sr = new StaticDispatch() ;
sr.sayHello(man);
sr.sayHello(woman);
}
}
//hello guy
//hello guy
我们看到分别传入man和woman这两个对象,输出的结果都是hello guy。说明都调用了代码中 sayHello(Human guy)方法。那么为什么会调用这个方法呢?下面我们先解释下这两个概念。
Human man = new Man() ;
这个代码的Human称为静态类型,而后面的Man称为实际类型。而方法调用时,是看传入方法的参数的静态类型,也就是Human作为依据来查找匹配的方法,因为这两个对象的静态类型都是Human,所有都是调用 sayHello(Human guy)方法。
通过自动装箱和扩大基础类型的值来选择方法重载
上面讲到了通过方法传入参数的静态类型来选择方法重载,下面讲解通过自动装箱和其他机制来选择方法重载。我们也是通过一个代码来解释。
public class Overload {
public static void sayHello(char arg){ //1
System.out.println("hello char");
}
public static void sayHello(int arg){//2
System.out.println("hello int");
}
public static void sayHello(long arg){//3
System.out.println("hello long");
}
public static void sayHello(float arg){//4
System.out.println("hello float");
}
public static void sayHello(double arg){//5
System.out.println("hello double");
}
public static void sayHello(Character arg){//6
System.out.println("hello Character");
}
public static void sayHello(Serializable arg){//7
System.out.println("hello Serializable");
}
public static void sayHello(Object arg){//8
System.out.println("hello object");
}
public static void sayHello(char... arg){//9
System.out.println("hello char...");
}
public static void main(String[] args) {
sayHello('a');
}
}
//hello char
上述代码毫无疑问会输出hello char。如果注释掉sayHello(char arg)方法后会调用sayHello(int arg)方法。这是当找不到方法参数为char类型的sayHello方法后,‘a’会代表97(字符‘a’的Unicode数值为十进制数字97),因此会调sayHello(int arg)方法。同样,在找不到int类型为参数的方法时,会调用long类型为参数的方法。查找顺序是char》int》long》float》double。当把这5个方法都注释掉,会调用参数类型为Character的sayHello方法。这时发生了一次自动装箱,'a’被包装为它的封装类型java.lang.Character。
当我们把Character类型为参数的方法也注释掉,会发现输出结果为hello Serializable。这时就会让人感觉疑惑了。其实当找不到Character类型为参数的方法后,会去寻找这个类型的父类,而Serializable正是Character的父类,因此把Serializable为参数类型的方法注释掉后,会输出hello object。因为Object是所有未显示继承其他类的父类。当我们把sayHello(Object arg)方法也注释掉,会调用sayHello(char… arg)方法。因此,可见变长参数的重载优先级是最低的。
总结下上面寻找方法重载的步骤
- 先按照参数类型为 char》int》long》float》double 顺序来寻找为方法
- 当上面的寻找不到时,会发生自动装箱,去选择自动装箱后的类,然后按照父类一层层去寻找
- 最后时可见变长参数的重载
好了,这就是方法重载的一些需要特别注意的地方。多说一句,以后再面试中再问到关于方法重载的时候,如果能回答到上面提到的这几点,那么面试官会对你刮目相看的,因为大多数人只知道方法重载的定义,而对其中的一些特性不了解。最后,我想说的是,基础的重要性,尤其是应届生去面试的时候,更是这样。有很多人走错了弯路,把学习的重点花在学习各种框架,其实以我的面试经历来说,真的大多数面试官喜欢考察基础,尤其是你对一些基础理解的比其他人深入的情况下,那么你得到offer的概率就会变大。