前言
在程序设计中经常会遇到要对方法进行重载或者重写的情况,下面就介绍一下重载和重写。
重载(Overloade)
重载出现的原因
任何程序设计语言都具备的一项重要特性就是对名字的运用。方法名就是给某个动作取的名字。通过使用名字,我们可以引用所有对象和方法。名字起的好可以使程序更易于理解和修改。
在大多数程序设计语言中要求为每个方法提供唯一的标识符。不能使用print()的方法显示了整数之后,又用一个名为print()的方法显示浮点数。即每个方法都要有唯一的名称。这是迫使出现重载方法
的理由之一。若是print()函数可以被重载了,那么就既可以输出整数也可以输出浮点数。又例如,我们要计算两个整数相加,我们可以设计方法为int add(int a, int b)。调用add(10,10)我们就可以知道是计算两个整数相加。此时,我们又想计算两个浮点数相加,因为add见名知意所以我们继续使用这个名字,那么就需要方法double add(double a, double c)。于是,add()方法就被重载
了。
在Java(和C++)里,构造器是强制重载方法出现的另一个原因。构造器的名字由类名决定,那么就只能有一个构造器。但是,又想使用多种方式创建对象又该怎么办呢?那么就只有重载构造器,使得同名不同参的构造器同时存在。
重载的规则
方法重载是在一个类里面,方法名相同,而参数不同,对返回值没有强制要求可以相同也可以不同。方法重载需要注意一下几点:
- 被重载的方法必须改变参数列表(参数个数或者类型不一样)
- 被重载的方法不介意改变返回类型和访问修饰符
- 被重载的方法可以声明新的或者更广的检查异常
- 方法能够在同一个类中或者在一个子类中被重载
- 不可以返回值类型作为分区重载函数的标准
区分重载方法
每个重载的方法都有独一无二的参数类型列表。所以在区分重载方法时,只能以类名和方法的形参列表作为标准。
那为什么不能使用方法的返回值来区分呢?
比如下面两个方法,虽然它们有相同的名字和形式参数,但是却很容易区分它们
void f(){}
int f() {return 1;}
只要编译器可以根据语境明确判断出语义,比如在int x = f()中,那么的确可以根据此区分方法。不过,有时你并不关心方法的返回值,你想要的是方法调用的其他效果,这时你可能会调用方法而忽略返回值。所以,向下面这样的调用方法:
f();
此时Java该怎样判断调用的是哪个方法呢?别人也无法理解这个代码的含义。因此,依据方法的返回值来区分重载方法是行不通的。
重写(Override)
重写出现的原因
先看一个例子
class Animal{
public void printWhoIAm(){System.out.println("Animal")}
}
public class Dog extends Animal{
public static void main(String args[]){
Dog dog = new Dog();
dog.printWhoIAm();
}
}
/*
output:
Animal
*/
当dog调用printWhoIAm()方法时,其实希望的是输出“dog”,而不是Animal。要实现输出“Dog”,想到了重载,可是重载要求被重载的方法具有不同的形参列表。这个方法不得行。dog调用的printWhoIAm()是父类中的,在子类中若是可以重写这个方法,那么就可以实现目的了。于是,重写(覆写)便产生了,为了解决父类方法在子类中不适用,而让子类重写方法的方式。
我们解决以上代码需求如下:
class Animal{
public void printWhoIAm(){
System.out.println("Animal")
}
}
public class Dog extends Animal{
//加上注解@Override可以强制进行重写的检查 防止自己重写错误
@Override
public void printWhoIAm(){
System.out.println("Dog")
}
public static void main(String args[]){
Dog dog = new Dog();
dog.printWhoIAm();
}
}
/*
output:
Dog
*/
重写的规则
-
重写是对父类允许访问的方法的实现过程进行重新编写,返回值不变或者为子类、形参列表不能改变并且访问控制权限不能严于父类。父类为default包访问权限,则子类就为public或者default;若父类是public,则子类必须为public。
-
子类可以重写父类的除了构造器的任何方法。构造器是和类名相同的,不能被子类继承,因此也不可以被重写。
-
重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常。例如: 父类的一个方法申明了一个检查异常 IOException,但是在重写这个方法的时候不能抛出 Exception 异常,因为 Exception 是 IOException 的父类,只能抛出 IOException 的子类异常。
子类可以重写父类中访问控制权限为private的方法吗?
答案是不可以。父类中的private方法对子类来说是不可见的,就算子类中完全按照重写要求定义方法,也不能算重写父类中的方法,实际上只是子类新增的一个方法。所以,只有非private方法才可以被重写。
super.方法()和this.方法()的区别
子类若是重写了父类的方法,那么父类原来的这个方法还可以被调用吗?
答案是可以的,使用super对父类的方法进行调用。
class Animal{
public void printWhoIAm(){System.out.println("Animal")}
}
public class Dog extends Animal{
//加上注解@Override可以强制进行重写的检查 防止自己重写错误
@Override
public void printWhoIAm(){System.out.println("Dog")}
public void print(){
super.printWhoIAm();
printWhoIAm();// this.printWhoIAm();
}
public static void main(String args[]){
Dog dog = new Dog();
dog.print();
}
}
/*
output:
Animal
Dog
*/
使用this.方法()
会先在本类中查找是否存在要调用的方法,如果不存在则查找父类中是否具备此方法。如果有则调用,否则出现编译时错误。
使用super.方法()
会明确表示调用父类中的方法,直接去父类寻找要调用的方法。
重载和重写的区别
区别 | 重载 | 重写 |
---|---|---|
参数列表 | 必须改 | 一定不能改 |
返回类型 | 可以修改 | 一定不能改 |
访问控制权限 | 可以修改 | 不能比父类严格 |
异常 | 可以修改 | 可以减少或删除,一定不能抛出新的或者更广的异常 |
发生范围 | 可以在一个类中也可以在子类中 | 在子类中 |
小结
需要注意的是重载和重写的定义形式。引用菜鸟教程的两句话和一张图结束。
-
方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载(Overloading)。
-
方法重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写(Overriding)
参考:
[1] Eckel B. Java编程思想(第四版)[M]. 北京: 机械工业出版社, 2007
[2] 菜鸟教程. Java 重写(Override)与重载(Overload) |[EB/OL]. /2019-02-18. https://www.runoob.com/java/java-override-overload.html.