方法的重载是Java语言中一项非常重要的机制。Java语言因为有了重载机制,使得程序员定义和调用方法都变得更加轻松。
4.2小节的Person类中定义了计算2个整数之和的add()方法,如果程序员为add()方法传递两个double型参数,则会因参数类型不兼容导致报错,如图4-4所示:

图4-4 参数类型不兼容
对比用来打印数据的println()方法,很多读者发现这个方法似乎接收任何类型的参数都不会出错,如图4-5所示:

图4-5 println()方法可接收多种类型参数
从图4-5可以清楚的看到,println()方法在被调用时可以接收各种类型的参数,所以读者肯定会问:为什么Person类的add()方法就只能接收int类型的参数呢?它能不能也像println()方法那样接收其他类型的参数呢?
为了弄清楚这个问题,我们需要查看一下println()方法的源代码。在IDEA中查看方法源代码的操作很简单,只需要把鼠标箭头悬停在方法名称上,按下Ctrl键,然后单击鼠标左键即可跳转到方法定义的源代码。通过查看println()方法的源代码可知,println()方法被定义在一个叫做PrintStream的类中,浏览PrintStream类的源代码,可以发现:在PrintStream类中定义了多个println()方法。这些方法的名称相同,但参数类型不同,如图4-6所示。

图4-6 PrintStream类中的多个println()方法
图4-6就能很好的解释为什么println()方法能够接收各种类型的参数。因为PrintStream类中有多个“版本”的println()方法,因此当在程序中调用println()方法时,如果传递的参数是int型,那么就调用println(int x)方法,当传递的参数是double型,就调用println(double x)方法,以此类推,传递何种类型的参数,就调用相应版本的println()方法。由于这些方法都叫println,因此给调用者带来的感觉就是:同一个方法可以接收多种类型的参数。而通过查看源代码我们弄清楚了事情的真相:这些println()其实是定义在同一个类中的多个不同的方法。
我们也可以仿照println()方法那样,在Person类中定义两个不同版本的add()方法。Java语言规定,同一个类中如果存在若干名称相同,参数不同的方法,这些同名方法之间形成的关系被称为 “重载”。 下面的【例04_03】就很好演示了Java语言的重载特性。
【例04_03 方法的重载】
Person.java
- public class Person {
- String name;//姓名
- char sex;//性别
- int age;//年龄
-
- //定义2个整数相加的方法
- int add(int a,int b) {
- int r = a+b;
- return r;
- }
-
- //定义2个浮点数相加的方法
- double add(double a,double b){
- double r = a+b;
- return r;
- }
- }
Exam04_03.java
- public class Exam04_03 {
- public static void main(String[] args) {
- Person p = new Person();
- int r1 = p.add(1, 2);//调用int型参数的add()
- double r2 = p.add(2.5, 3.5);//调用double型参数的add()
- System.out.println("两个整数相加结果是:"+r1);
- System.out.println("两个浮点数相加结果是:"+r2);
- }
- }
在例【例04_03】的Person类中,我们添加了参数为double型的add()方法,它与参数为int型的add()方法就形成了重载关系。添加了该方法之后,我们既可以给add()方法传递两个int型参数,也可以给它传递两个double型参数,两种调用形式都不会导致语法错误,并且能够得到正确的运行结果。【例04_03】的运行结果如图4-7所示:

图4-7 【例04_03】的运行结果
需要注意:方法是否形成重载关系,仅仅与方法名称和参数有关,与方法的返回值类型无关,因此无论在任何情况下,都不能以方法的返回值类型作为方法是否形成重载关系的判断依据。特别需要提醒各位读者,千万不要把 “参数不同”这四个字简单的理解为“形式参数的名称不同”。 所谓“参数不同”其真正含义是指虚拟机在调用这些同名方法时,能够通过方法参数的定义形式判断出要调用哪一个方法,例如:
- int add(int a,int b){
- int r = a+b;
- return r;
- }
-
-
-
- int add(int x,int y){
- int r = x+y;
- return r;;
- }
以上代码中所定义的两个方法都叫add,它们的参数分别叫做a、b和x、y。如果仅从参数名称来看,这两个add()方法的参数确实不相同,但当在程序中出现语句“p.add(2,3);”时,虚拟机既可以调用add(int a,int b),也可以调用add(int x,int y),它无法根据参数的定义形式区分出应该调用哪一个add()方法,因此,以上代码中所定义的两个add()方法并不构成重载关系,编译器会认为这是方法出现了重复定义,因此会给出语法错误提示。
当一个类中出现了重载方法时,在实际执行代码过程中虚拟机总是会调用那个“最适合”的版本。例如在Person类中定义了如下两个版本的add()方法:
- int add(int a,int b){
- int r = a+b;
- return r;
- }
-
-
-
- double add(double a,double b){
- double r = a+b;
- return r;
- }
当程序中出现语句“p.add(2,3);”时,根据赋值运算规则,虚拟机既可以调用add(int a,int b),也可以调用add(double a, double b),但显然调用add(int a,int b)更加合适,因为实际参数的类型都是int型,它们与add(int a,int b)这个版本的方法吻合程度最高。类中没有定义add(int a,int b)方法的,才会退而求其次调用add(double a, double b)。
还有一种很特殊的情况也需要读者注意,那就是:当调用同名方法时,两个甚至是多个版本的方法合适程度相同,此时虚拟机也无法决定调用哪个方法。例如在一个类中如果定义了如下两个版本的add()方法:
- double add(double a,int b){
- double r = a+b;
- return r;
- }
-
-
-
- double add(int a,double b){
- double r = a+b;
- return r;
- }
首先可以肯定,这两个add()方法的参数定义形式不同,因此它们之间具有重载关系。但是当程序中出现了语句“p.add(2,3);”时,虽然调用任何一个add()方法都不是最合适的,但是根据自动类型转换原则,这两个方法又都能接收两个int型的参数。只不过这两个add()方法在接收参数时,一个方法把实际参数2转换成2.0,另一个方法把实际参数3转换成3.0。此时,虚拟机同样无法决定调用哪一个add()方法,所以编译器会在语句“p.add(2,3);”的下方提示语法错误。在这种情况下,只能依靠程序员修改代码来帮助虚拟机做出调用决定。例如,程序员可以把原来的语句修改成“p.add(2.0,3);”,这样根据“最适合”原则,虚拟机会调用add(double a,int b)这个方法来完成两个数字的加法运算。
以上这部分内容详细讲解了重载的语法规则以及在程序中使用这种技术时需要注意的细节。那么,Java语言中引入重载机制有什么意义呢?从根本上来说,重载的引入就是为了让开发者编码更容易。还以我们在程序中最常用的println()方法来举例:为了打印不同类型的数据,就需要在类中定义多个打印数据的方法。如果Java语言没有引入重载机制,就要求这些方法的名称必须做到互不相同,否则虚拟机无法正确识别这些方法。由于被打印数据的类型非常多,所以要定义出至少10个名称不同的方法。开发者在调用这些方法时,必须能够根据参数类型准确的写出与之对应的方法名称,这显然增加了编码的难度。尤其对于早期使用像记事本这样的纯文本编辑工具完成开发的那些程序员,没有智能化IDE的提示功能,必须完全依靠人力完成各个方法的准确调用,这显然非常困难。而Java语言引入重载机制,无论打印何种类型的数据,程序员只需要调用一个println()方法就能实现,编码的难度大大降低,同时也在很大程度上提升了开发效率。