导语
昨天看 spring 源码时发现重载和覆盖用的很多, 又想起之前面试实习居然混淆了, 平时不爱看书, 又不总结, 对于基础知识感觉懂又不全懂.
直接看代码
一个简单的例子:
- public interface Animal {
- default Animal getInstance() {return this;}
- default void perform(){System.out.println("default perform...");}
- }
- public class Dog implements Animal {
- @Override
- public void perform() {System.out.println("dog is in the performance...");}
- }
- public class Cat implements Animal {
- @Override
- public Cat getInstance() {return this;}
- }
- public class Main {
- public static void main(String[] args) {
- Animal animal = new Dog();
- animal.perform();
- }
- }
这个例子很简单, 但还是要拿出来, 主要是 Cat 复写 getInstance 时, 改变了其返回值, 依旧是覆盖. 但是如果改成不是原返回值类型的子类时则会报错, 不是重载也不是继承, 直接是函数重名错误. 那么就可以推广到, 一个类无法实现了有方法重名且返回值没有继承关系的两个接口.
复杂点的例子:
- public abstract class AbstractAnimal implements Animal{
- @Override
- """
- 记住, 我们是商人.
- """
- public abstract Animal getInstance() throws Exception;
- @Override
- public final void perform (){
- try {
- this.getInstance().perform();
- } catch (Exception e) {
- System.out.println("Animals are missing...");
- }
- }
- protect abstract buyAnimals();
- }
- public class Zookeeper extends AbstractAnimal {
- public static int times = 0;
- List<Animal> animals;
- public Zookeeper() {
- this.buyAnimals();
- }
- @Override
- public Animal getInstance() throws Exception {
- if(animals== null || animals.size()==0)
- throw new Exception("Zookeeper has no animals...");
- times++;
- return animals.get(times % animals.size());
- }
- public final void perform(int index) {
- if(animals.size() <= index)
- super.perform();
- else
- this.animals.get(index).perform();
- }
- public void perform(Class type) {
- for(Animal a: animals) {
- if(a.getClass() == type) {
- a.perform();
- return;
- }
- }
- System.out.println("no such animals:" + type.getClass().getSimpleName()) ;
- }
- /**
- public Animal perform(int index) {
- if(animals.size() <= index)
- index = animals.size();
- Animal animal = animals.get(index);
- animal.perform();
- return animal;
- }
- */
- @Override
- public void buyAnimals() {
- if(animals == null)
- animals = new ArrayList<>();
- this.animals.add(new Cat());
- this.animals.add(new Dog());
- this.animals.add(new Cat());
- }
- }
- public class Main {
- public static void main(String[] args) {
- Animal zookeeper = new Zookeeper();
- zookeeper.perform();
- // zookeeper.perform(3); 无法使用
- zookeeper.perform();
- Zookeeper zookeeper1 = new Zookeeper();
- zookeeper1.perform();
- zookeeper1.perform();
- zookeeper1.perform(2)
- }
- }
Zookeeper 继承自 AbstractAnimal, 而它的父亲 AbstractAnimal 很聪明, 它把 getInstance() 覆盖为抽象方法, 出于害怕把 buyAnimals() 权限设置成 protect, 并且覆盖 Animal 中的 perform 方法并规定 perform 无法再被它子类覆盖. 继承后的 Zookeeper 知道父亲的用意, 但公开地购买很多的 Animal, 实现了父亲的 getInstance, 让买来的 Animal 轮流替他 perform, 并且又重载出很多花样的 perform.
重载和覆盖做了什么?
个人觉得:
覆盖做的是去掉层层继承与实现中会被 java 虚拟机认为是同一个函数的.
重载做的是如何让虚拟机认识这两个函数虽然名称相同, 但实质不同.
我们看一行很常用的代码: a.f(argv), 其中 a 为某个实例对象, f 为方法名, argv 为若干个参数或者空
假如我们重写父类中的方法不会被覆盖掉, 按照继承可以获得父类所有可访问的方法, 那么子类中将会有两个方法假设名为 f, 且参数列表相同, 那么调用 f 时该如何抉择呢? 显然只给的 a,f,argv 三个参数虚拟机无法去抉择, 必须覆盖掉一个. 但是我们确实有写几个名称相同的方法的需求, 此时 a 和 f 都是相同的, 那么只有通过 argv 来区分了, 这个过程大概就是重载吧.
先看函数名相同各种的情况:
参数列表 (参数列表指参数类型顺序, 个数)
final 修饰
权限修饰符
返回值类型
abstract 修饰
函数重载时可以发生在本类中和本类与父类中, 本类与父接口的默认方法中, 参数列表必定不同, 和 final 修饰, 权限修饰, 返回值类型, abstract 修饰无关系.
方法覆盖只能发生在子类与父类或子类与父接口的默认方法间, 函数名相同, 参数列表相同, 返回值类型可以不同但必须是其原类型的子类, 不能覆盖 final 修饰的方法, abstract 修饰符符合 java 规范即可.
几种特别注意的函数名相同:
1. 子类覆盖父类方法 do() 时, 提高了该方法的权限:
- Father f = new Son();
- f.do() // 因为 Son 中权限的提高导致 do() 无法调用
2. 参数列表相同, 但返回值不是父类中定义类型或其子类
Father f = new Son() 方法重名.
A a = f.getA() // 因为被 Son 覆盖为返回 B 类型, 这不是坑爹吗?
3. 参数列表相同, 返回值是父类中定义类型的子类
此时 Father f = new Son()
A a = f.getA() 形如 A a = new B() 形如 Father f = new Son() , 没毛病.
4. 抽象实现, 将父类方法改为抽象实现
此时子类必定是个抽象类, 其抽象实现的父类方法将由子类的子类实现. 没毛病.
5. 与父类方法参数列表不同, 返回值相同.
我给你三个参数, 你却去调用父类两个参数的方法? 显然是重载.
总结
一个对象实例中有名称相同的两个方法, 若参数列表相同, 且两方法不在同一类中, 要么是覆盖要么是不符合 java 规范; 若参数列表不同, 要么是重载, 要么是不符合 java 规范.
来源: https://yq.aliyun.com/articles/667440