###一、方法重载简介

方法重载: 当两个(或多个)方法的名称相同,而参数的对应类型或个数不同时,我们就说方法重载了。当然,编译器也能识别出来。

编译器是如何识别调用了哪个方法?

在往下讲前,我们先来了解一下:编译器是怎么才能识别出程序调用了那个方法。其实,这个问题就是在问:在调用方法处,编译器能得到调用方法的什么信息,从而能找到对应的方法?我们一般的方法调用是这样的:

method( vars );

也就是说,方法调用处,一共为编译器提供两个信息:方法名、参数列表。
所以,编译器只能通过 方法名 和 参数列表 来识别调用方法

有一道面试题问:为什么不能通过返回类型来重载方法?
就是上面所说的,方法调用处并没有提供返回类型的信息,所以当多个方法只有返回类型不一样时,编译器就不知道调用了那个方法了。

我们已经知道了编译器是怎么识别方法的了,而对于方法重载,其要求方法名是一样的,那么我们只需要关注 参数列表 便可以了参数列表区分,或者说重载方法的区分:

  • 参数的个数
  • 参数的类型
  • 参数的顺序

###二、方法重载的匹配选择

方法重载后,方法调用处可能会遇到应该选择哪个重载方法的问题,如果只有唯一个重载方法可以匹配,那么就没问题。然而,大部分情况却是有多个重载方法是可以匹配的,那么这时候就应该选择最合适的重载方法.

匹配最合适、最明确的重载方法,其实就是实参列表去匹配当前重载方法中形参列表,寻找与实参列表最相近的形参列表
##1、基本类型之间的重载

对于基本类型来说,从“短类型”扩展成“长类型”是默认允许、自动进行的,这就可能造成了实参可能匹配到多个“长类型”的形参,看个简单例子:

 public static void main(String[] args) {short s = 4;m(s);
}public static void m(int x){//方法一System.out.println("重载方法一");
}public static void m(float x){//方法二System.out.println("重载方法二");
}

运行结果

重载方法一

short类型 可以默认自动转换成int、'float’类型。但m(s)真正匹配选择的是m(int x)方法,而不是形参长度更长的m(float x)。所以可以看出,基本类型的形参匹配规则是: 如果没有匹配到精确类型的形参,则优先匹配 存储长度(范围)大于且是最接近实参的存储长度的形参,从而确定调用哪个重载方法
##2、引用类型间的重载

对于引用类型来说,可以匹配到多个重载方法的原因是:引用类型的对象进行类型上转也是JVM默认自动进行的,那么就可能匹配多个祖先类型的形参看下面的例子:

public class Test_3 {public static void main(String[] args)  {Children children = new Children();someMethod(children);
}   public static void someMethod(Ancestor an) {//重载方法1System.out.println("this is Ancestor Method!");
}public static void someMethod(Parent an) {//重载方法2System.out.println("this is Parent Method!");
}
}//3个具有继承关系的类
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);// test1Integer i = 10;overloadMethod(i);//test2overloadMethod(s,s);//test3
}   public static void overloadMethod(int a) { //m1System.out.println("调用  overloadMethod(int)");
}public static void overloadMethod(Short in) {//m2System.out.println("调用  overloadMethod(short)");
}public static void overloadMethod(int a,int b) {//m3System.out.println("调用  overloadMethod(int,int)");
}public static void overloadMethod(short... s) { //m4System.out.println("调用  overloadMethod(short...)");
}public static void overloadMethod(Integer... i) {//m5System.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) {//m1System.out.println("调用了m(int,Short)");
}public static void m(float f,short s) {//m2System.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 <T> void m(T t) {//m1System.out.println("调用了<T> void m(T)");
}public static <T extends Runnable> void m(T t) {//m2System.out.println("调用了<T extends Runnable> 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) {//m1System.out.println("调用了m(int,Short)");
}public static void m(float f,int c) {//m2System.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);//编译不通过

##重写 与 重载的区别

  • 重写是针对父类与子类间的方法,即必须先得继承父类的方法。而重载则没有这种限制。
  • 重写要求方法的 而方法重载则只需要 方法名相同,参数列表不同就行了。
  • 方法重载时,方法的调用是在编译时期就已经确定了调用那个方法;方法重写,则要在运行时,才能确定调用的是子类还是父类的方法。

作者:jinggod
出处:http://www.cnblogs.com/jinggod/p/8503150.html

类与接口(四)方法重载解析相关推荐

  1. java泛型方法 通配符_Java泛型教程–示例类,接口,方法,通配符等

    java泛型方法 通配符 泛型是Java编程的核心功能之一,它是Java 5中引入的.如果您使用的是Java Collections ,并且版本5或更高版本,则可以肯定使用了它. 将泛型与集合类一起使 ...

  2. Java泛型教程–示例类,接口,方法,通配符等

    泛型是Java编程的核心功能之一,它是Java 5中引入的.如果您使用的是Java Collections ,并且版本5或更高版本,则可以肯定使用了它. 在集合类中使用泛型非常容易,但是它提供了比仅创 ...

  3. Mybatis中mapper接口里方法重载的实现

    看了网上的很多文章,说mapper接口里不能写重载方法,感觉这种说法不对,mapper接口是可以实现重载方法的. 实现方法 例如: package mapper;import pojo.User;im ...

  4. java接口的默认方法,实现类调用接口默认方法

    概述 Java8带来了一些全新的特性,包括lambda表达式.函数接口.方法引用.流.可选方法.接口中的静态方法和默认方法. 在本文中,我们将深入讨论为什么java8接口新增了默认方法,如何使用默认方 ...

  5. Java泛型(类、接口、方法)及通配符

    什么是泛型? 泛型,即"参数化类型".例如:定义方法时有形参,然后调用此方法时传递实参.参数化类型就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形 ...

  6. 简单的类实现接口的方法

    public interface carepet {public void food();public void play(); }public class Worker implements car ...

  7. java 泛型示例_Java泛型示例教程–泛型方法,类,接口

    java 泛型示例 Java Genrics is one of the most important features introduced in Java 5. Java Genrics是Java ...

  8. java基础-类-抽象类-接口(自学笔记)

    一.类 class 定义:具有相同属性(特性)和方法(行为)的一类事物 的抽象的总称 例如:人类,动物,车,屌丝类.是一种类别,世界上所有的事物都可以分类,在计算机中java都能进行对应和体现 类的三 ...

  9. 【JDK8 新特性2】JDK 8 接口默认方法/静态方法

    目录 1.接口默认方法 1.1 接口默认方法的定义格式 1.2 接口默认方法的使用 1.2.1 实现类直接调用接口默认方法 1.2.2 实现类重写接口默认方法 2.接口静态方法 2.1 接口静态方法的 ...

最新文章

  1. 2014 Super Training #7 F Power of Fibonacci --数学+逆元+快速幂
  2. linux tcp keepalive,[20170504]Linux TCP keepalive timers.txt
  3. 计算机网络电缆被拔出是怎么办,网络电缆被拔出是什么意思?网络电缆被拔出的修复办法...
  4. Linux存储保护,谈谈Linux中的存储保护
  5. 几个常用SQL2000语句
  6. 2014年12月份工作日学习计划
  7. 使用QRCode轻松实现二维码扫描下载的功能
  8. os系统安装python_在MacOS系统上安装疯子Python
  9. 笔记本独显和集显linux,笔记本电脑独显不见了的解决方法介绍
  10. nmap扫描之端口“filtered“被过滤的分析
  11. selenium基础②:模拟QQ空间登录
  12. 如何在ESXi7系列镜像里添加网卡vib驱动?
  13. 分支-07. 比较大小(10)
  14. 调用百度h5活体检测
  15. [完结]saliency filters精读之permutohedral lattice
  16. Linux下C/C++实现(网络流量分析-NTA)
  17. 28、完美的RestApi
  18. websocket连接不成功的原因
  19. 试卷模板php,试卷Latex模板
  20. 酒店管理与计算机未来的结合,智慧酒店:未来酒店发展新方向

热门文章

  1. castle 组件化开发应用配制示例
  2. 关于ESP8266 GPIO中断使用的总结
  3. 神经网络入门-MLP, RNN, BiRNN
  4. 设计模式总结 (5)行为型模式续
  5. 石墨烯区块链(4)API
  6. 数学建模——灰色预测模型Python代码
  7. 【Flask】Jinja2之模板继承
  8. optee系统服务/service的实现方式
  9. [ARM异常]-ARM Core如何响应中断的
  10. recv函数返回值说明