一、方法重装概述
方法重载:如果两个或多个方法的名称相同,且参数的相应类型或数量不同,则称为方法重载。 当然,编译器也能识别。
编译器如何识别调用了哪些方法?
在继续之前,让我们先了解编译器是如何识别程序调用了其方法的。 其实,这个问题是,在调用方法时,编译器可以得到调用方法的什么信息,并找到相应的方法? 我们常见的方法调用如下:
方法(价值);
也就是说,方法调用方向编译器提供方法名称、参数列表这两个信息。
因此,编译器只能从方法名称和参数列表中标识调用方法。
一个面试问题是:“为什么不能用返回型重载方法?
如上所述,由于方法调用方不提供有关返回类型的信息,因此如果多个方法中只有返回类型不同,编译器就不知道调用了该方法。
虽然我们已经知道编译器是如何识别方法的,但对于方法重载,方法名称必须相同。 只要关注参数列表就可以了。 区分参数列表或区分重载方法:
参数的个数
参数类型
参数顺序
二、方法重载匹配选择
方法重载后,可能会出现在方法调用方应该选择哪种重载方法的问题,但如果只有唯一的重载方法匹配,则没有问题。 然而,在许多情况下,多个过载方法是匹配的,因此在这种情况下应该选择最佳过载方法。
匹配最合适、最明确的重载方法的是,实际上实参列表与当前重载方法中的形参列表相匹配,并且寻找最接近实参列表的形参列表
1、基本类型之间的重载
对于基本类型,缺省情况下允许从“短类型”扩展到“长类型”,并且会自动进行扩展。 这样,实参就有可能与多个“长类型”的形参一致。 让我们来看一个简单的例子。
publicstaticvoidmain (字符串[ ] args ) {
短整型=4;
m(s;
}
公共统计(intx )//方法1
系统. out.println (重载方法1 );
}
公共统计(浮动)//方法2
系统. out.println (重载方法2 );
}
执行结果:
重载方法1
缺省情况下,可以自动将短类型转换为int、“浮动”类型。 然而,m(s )真正匹配的是m ) m(int x )方法,而不是波形参数长度长的m ) m(float x )。 因此可知,基本型s形匹配规则是在与正确型s形不一致的情况下,优先匹配存储器长度(范围)大且最接近实际参数的存储器长度的s形,来决定调用哪个重载方法
2、引用类型之间的重载
对于引用类型,可以匹配多个重载方法的原因是,缺省情况下,JVM还会自动进行引用类型对象类型的上转换,因此它可能匹配多个祖先类型的形式。 请参阅以下示例。
公共类test _3{
publicstaticvoidmain (字符串[ ] args ) {
Children children=new Children (;
somemethod(Children );
}
publicstaticvoidsomethod (ancestor an )//重载方法1
system.out.println (' thisisancestormethod!' );
}
公共服务语音(Parentan )//重载方法2
system.out.println (' thisisparentmethod!' );
}
}
//具有继承关系的三个类
class Ancestor{//祖先类
}
class Parent extends Ancestor{//由父类、Ancestor继承
}
在class Children extends Parent{//子类中,由Parent继承
}
执行结果:
this is Parent Method!
引用类型与基本类型一样,选择“最明确的方法”,选择引用类型之间最明确的重载方法的规则是:重载方法的引用类型与实参不一致,实参在继承树结构上优先匹配与实参类型最近的实参,则存在该实参的重载方法
3、自动装箱开箱、可变参数型
开箱及可变参数列表的处理由编译器自动处理。 也就是说,默认情况下自动进行。 这将确保实际参数列表与多个形状参数列表相匹配,并与多种重载方法相匹配。
本节介绍如何重新加载基本类型、参考类型和自动装箱拆箱可变参数的匹配优先级。
请看下面的例子。 就是这个例子
包括很多情况:public class Test_3 {
public static void main(String[] args) {
short s = 5;
overloadMethod(s);// test1
Integer i = 10;
overloadMethod(i);//test2
overloadMethod(s,s);//test3
}
public static void overloadMethod(int a) { //m1
System.out.println("调用 overloadMethod(int)");
}
public static void overloadMethod(Short in) {//m2
System.out.println("调用 overloadMethod(short)");
}
public static void overloadMethod(int a,int b) {//m3
System.out.println("调用 overloadMethod(int,int)");
}
public static void overloadMethod(short... s) { //m4
System.out.println("调用 overloadMethod(short...)");
}
public static void overloadMethod(Integer... i) {//m5
System.out.println("调用 overloadMethod(Integer...)");
}
}
运行结果
调用 overloadMethod(int)
调用 overloadMethod(int)
调用 overloadMethod(int,int)
我们来分析一下上面的例子中,方法调用处可以匹配到的方法:
test1 处的方法调用可以匹配的重载方法有:m1(基本类型的短类型自动转为长类型)、m2(自动装箱)、m4(可变参数列表)
test2 处的方法调用可以匹配的重载方法有:m1(自动拆箱)、m5(可变参数列表);
test3 处的方法调用可以匹配的重载方法有:m3(基本类型的短类型自动转换成长类型)、m4(可变参数列表)
查看输出结果,发现:test1处选择了m1、test2选择了m1,test3选择了m3。
根据这样的结果,也就是这几种形参匹配规则还是有个匹配的顺序的。对重载方法的选择作以下总结:先按照实参的类型(基本类型或引用类型)对应匹配规则,进行查找最相近的形参列表,从而找到最明确的重载方法;找不到,则执行第二步;
对实参进行装箱或拆箱转换(前提是实参是基本类型或者是包装类),再安按照转换得到的类型进行匹配形参的类型(形参类型与转换类型要一致,特别注意基本类型);找不到,则执行第三步;
匹配形参是可变参数的重载方法,此时,形参的类型可以是 实参的类型以及通过 基本类型的短转长、自动装箱拆箱、祖先类型 得到的转换类型。
将上面的总结再简化一下,可以简化成 重载方法的形参匹配规则的优先级:
当前类型(基本类型或引用类型)的匹配规则 > 自动装箱拆箱 > 可变参数列表
再看一个例子:
public class MyTest {
public static void main(String[] args) {
int a = 5;
short s = 8;
m(a,s);
}
public static void m(int a,Short b) {//m1
System.out.println("调用了m(int,Short)");
}
public static void m(float f,short s) {//m2
System.out.println("调用了m(float,short)");
}
}
运行结果:
调用了m(float,short)
分析: 实参都是基本类型,优先考虑形参列表都是基本类型的重载方法,找不到才考虑自动装箱拆箱
4、泛型方法的重载
泛型方法的重载规则: 将泛型方法的类型变量擦除,然后与非泛型方法一样,按照上面所说的三种规则一一匹配
public static void main(String[] args) {
//创建Runnable对象
Runnable r = new Runnable() { public void run(){} };
//调用泛型方法
m(r);
}
public static void m(T t) {//m1
System.out.println("调用了 void m(T)");
}
public static void m(T t) {//m2
System.out.println("调用了 void m(T t)");
}
运行结果:
调用了 void m(T t)
上面的两个泛型方法m(T t)进行类型擦除后是:
public static void m(Object t);
public static void m(Runnable t);
显然,调用方法应该是m2,与运行结果相符;
5. 没法确定的重载方法调用
尽管编译器会按照上面所说的三种优先级别去让实参匹配形参,然而匹配的结果却不一定是唯一的,也就是说会匹配到多个方法,从而无法确定调用那个方法,编译失败
情况一: 实参列表的所有最佳匹配的形参不在同一个方法中
public class MyTest {
public static void main(String[] args) {
int aa = 5;
short ss = 8;
m(aa,ss);//编译不通过,无法确定调用了那个重载方法
}
public static void m(int a,double b) {//m1
System.out.println("调用了m(int,Short)");
}
public static void m(float f,int c) {//m2
System.out.println("调用了m(float,short)");
}
}
分析:
m(aa,ss)的调用编译失败,因为实参aa的最佳匹配m(int,double)的第一个形参,而实参ss的最佳匹配则是m(float,short)的第二个形参。
因此,实参列表的(aa,ss)的最佳形参类型匹配分开在了两个重载方法中。
注意一下,即使某个重载方法的形参列表包含最多的最相近的形参类型,只要不是全部,那么依旧无法确定调用了哪个重载方法。
情况二: 可变参数列表的特殊性 -- 无法根据可变参数的类型来重载方法
public static void m(short... s) {}
public static void m(Short... s) {}
public static void m(int... s) {}
调用测试例子:
short s = 8;
Short sl = 10;
m(s,s);//编译不通过
m(s,sl);//编译不通过
m(sl,sl);//编译不通过
重写 与 重载的区别
重写是针对父类与子类间的方法,即必须先得继承父类的方法。而重载则没有这种限制。
重写要求方法的 而方法重载则只需要 方法名相同,参数列表不同就行了。
方法重载时,方法的调用是在编译时期就已经确定了调用那个方法;方法重写,则要在运行时,才能确定调用的是子类还是父类的方法。