乐趣区

关于java:大厂面试题系列重载Overload和重写Override的区别重载的方法能否根据返回类型进行区分

面试题:重载(Overload)和重写(Override)的区别。重载的办法是否依据返回类型进行辨别

面试官考察点猜测

这道题纯正只是考查基础理论常识,对理论开发工作中没有太多的指导意义,毕竟编辑器都有语法提醒性能,如果没写正确,会有谬误提醒。

背景常识详解

对于重载(Overload)和重写(Override),在理论开发中应用十分频繁,波及到的背景常识并不难。

重写

重写是子类对父类的容许拜访的办法的实现过程进行从新编写, 返回值和形参都不能扭转。即外壳不变,外围重写!

重写是产生在类的继承关系,或者类的实现关系中的,重写后的办法和原办法须要放弃完全相同的返回值类型、办法名、参数个数以及参数类型,简略来说,就是子类重写的办法必须和父类放弃完全一致

类的继承关系

咱们来看上面这个基于继承关系的例子。

class Animal{public void move(){System.out.println("动物能够挪动");
   }
}

class Bird extends Animal{public void move(){System.out.println("鸟能够飞");
   }
}
class Dog extends Animal{public void move(){System.out.println("狗能够跑")
  }
}

public class TestMain{public static void main(String args[]){Animal a = new Animal(); // Animal 对象
      Animal b = new Bird(); //Bird 对象
      Animal c = new Dog(); // Dog 对象

      a.move();// 执行 Animal 类的办法
             b.move(); // 执行 Bird 类的办法
      c.move();// 执行 Dog 类的办法}
}

上述程序运行的后果

动物能够挪动
鸟能够飞
狗能够跑

在这个案例中,Animal是一个属于动物的抽象类,它定义了一个办法move()。示意动物的具备的行为。

而动物只是一个泛类别,具体到某种动物时,行为形式是不同的,因而定义了 BirdDoc,别离继承了 Animal 这个类,并且重写了 move() 办法,别离实现这两种动物的行为形式。

重写的益处在于子类能够依据须要,定义特定于本人的行为。也就是说子类可能依据须要实现父类的办法。

在类继承关系中,父类的非形象办法,子类是不强制要求重写的。在理论利用中,如果重写了父类的办法,并且实例对象的援用指向的是子类时,JVM 会主动调用子类重写的办法,此时,父类的办法齐全被屏蔽了。就像后面测试的代码。

父类援用指向子类实现 Dog(),此时调用c.move() 办法,只会调用到 Dog 类中的 move() 办法。如果 Dog 子类没有重写 move() 办法,则会调用父类 Animalmove()办法。

Animal c = new Dog(); // Dog 对象

c.move();// 执行 Dog 类的办法

在有些状况下,子类重写了父类的办法,咱们心愿在调用子类重写办法的同时,依然可能调用到父类被重写的办法,怎么实现?

Super 关键字

当须要在子类中调用父类的被重写办法时,要应用 super 关键字。

class Animal{public void move(){System.out.println("动物能够挪动");
   }
}

class Bird extends Animal{public void move(){super.move(); // 减少 super 调用
      System.out.println("鸟能够飞");
   }
}

}

public class TestMain{public static void main(String args[]){Animal b = new Bird(); //Bird 对象  
      b.move();// 执行 Bird 类的办法}
}

运行后果如下:

动物能够挪动
鸟能够飞

办法的重写规定

总结一下,在 Java 中,办法重写的规定。

  • 参数列表与被重写办法的参数列表必须完全相同。
  • 返回类型与被重写办法的返回类型能够不雷同,然而必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本能够不同)。
  • 拜访权限不能比父类中被重写的办法的拜访权限更低。例如:如果父类的一个办法被申明为 public,那么在子类中重写该办法就不能申明为 protected。
  • 父类的成员办法只能被它的子类重写。
  • 申明为 final 的办法不能被重写。
  • 申明为 static 的办法不能被重写,然而可能被再次申明。
  • 子类和父类在同一个包中,那么子类能够重写父类所有办法,除了申明为 private 和 final 的办法。
  • 子类和父类不在同一个包中,那么子类只可能重写父类的申明为 public 和 protected 的非 final 办法。
  • 重写的办法可能抛出任何非强制异样,无论被重写的办法是否抛出异样。然而,重写的办法不能抛出新的强制性异样,或者比被重写办法申明的更宽泛的强制性异样,反之则能够。
  • 构造方法不能被重写。
  • 如果不能继承一个类,则不能重写该类的办法。

基于接口实现的重写

基于接口实现的重写,在理论利用中,应用十分频繁,以线程实现为例,如图所示,示意 Thread 和 Runnable 的类关系图。

Runnable 是一个接口,它定义了线程的执行办法,代码如下:

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();}

在理论利用中,咱们能够间接继承这个接口来申明一个线程。

Thread,是一个一般的线程类,它实现了 Runnable 接口,并且重写了 Runnable 这个接口的 run 办法,这里这么设计的目标是:防止 Java 中 一个类只能实现一个接口这一规定 导致,如果一个类曾经继承了其余的接口,然而又想要去实现线程时的问题。

public
class Thread implements Runnable {
    @Override
    public void run() {if (target != null) {target.run();
        }
    }
}

因为接口只是用来做标准设计,用来形容某个对象具备什么行为,然而它并没有具体的实现,因而如果须要申明一个线程,就须要实现该接口并且重写外面的形象办法(接口中未实现的办法都是形象的,子类必须要重写)。

Thread 类中重写了 Runnable 中的 run 办法,该办法调用了 target.run()。这个target 是真正的线程业务实现,Thread 只是一个委派设计模式。

因而,如果咱们想通过继承 Thread 来实现线程,则须要依照如下代码的写法来实现,其中 target 就是代表着子类的 App 这个对象实例。

public class App extends Thread{
    @Override
    public void run() {//doSomething}
}

因为接口只是一种行为规范,自身不提供实现,因而实现接口的子类,都“必须”要重写父类的办法,这个和类继承是有区别的。

重载

重载(overloading) 是在一个类外面,办法名字雷同,而参数不同。返回类型能够雷同也能够不同。

每个重载的办法(或者构造函数)都必须有一个举世无双的参数类型列表。

最罕用的中央就是结构器的重载,比方在 ThreadPoolExecutor 线程池的实现类中,可看到如下的重载办法。

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {}
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {}
 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {}

办法重载的益处就是让类以对立的形式解决不同类型的一种伎俩,调用办法时通过传递给他们的不同个数和类型的参数来决定具体应用哪个办法,这就是多态性。

它的特点是:重载产生在本类,办法名雷同,参数列表不同,与返回值无关,只和办法名,参数的类型相干。

办法重载时,办法之间须要存在肯定的分割,因为这样能够进步程序的可读性,并且咱们个别只重载性能类似的办法。

重载规定

  • 被重载的办法必须扭转参数列表(参数个数或类型不一样);
  • 被重载的办法能够扭转返回类型;
  • 被重载的办法能够扭转拜访修饰符;
  • 被重载的办法能够申明新的或更广的查看异样;
  • 办法可能在同一个类中或者在一个子类中被重载。
  • 无奈以返回值类型作为重载函数的辨别规范。

问题解答

了解了上述知识点当前,再来看这道面试题。

面试题:重载(Overload)和重写(Override)的区别。重载的办法是否依据返回类型进行辨别

区别:

  1. 办法重载是一个类中定义了多个办法名雷同, 而他们的参数的数量不同或数量雷同而类型和秩序不同, 则称为办法的重载(Overloading)。
  2. 办法重写是在子类存在办法与父类的办法的名字雷同, 而且参数的个数与类型一样, 返回值也一样的办法, 就称为重写(Overriding)。
  3. 办法重载是一个类的多态性体现, 而办法重写是子类与父类的一种多态性体现。

重载办法是否可能依据返回类型进行辨别

重载办法无奈依据类型来辨别,它只能通过参数类型、参数个数来辨别,然而对于重载的办法,是容许批改返回值类型、异样类型、拜访等级,然而不能只依据这些类型类做重载。

为什们不能仅依据返回类型来辨别重载呢?

起因是,在调用指标办法时,是无奈指定返回值类型信息的,这个时候编译器并不知道你要调用哪个函数。

比方在上面这段代码中,当调用 max(1,2); 时无奈确定调用的是哪个,单从这一点上来说,仅返回值类型不同的重载是不应该容许的。

float max(int a, int b);
int max(int a, int b);

可能有同学会问,如果让编译器可能依据上下文语境来判断呢?比方像上面这段代码。

float x=max(1,2);
int y=max(2,3);

在理论开发中,很多时候会存在这样一种办法调用max(1,2),并不会去申明返回值,因为这种状况的存在,所以这个实践也不能实现。

函数的返回值只是作为函数运行之后的一个“状态”他是放弃办法的调用者与被调用者进行通信的要害。并不能作为某个办法的“标识”

问题总结

这个问题,其实是属于那种,你不问我,我肯定会认为本人晓得,而且在工作开发中也能应用不会出问题,然而你一问我,我肯定会懵逼,不是因为真的不懂,而是不晓得怎么去组织语言来形容这两个概念。

倡议大家参考“费曼学习法”,就是把这篇文章学到的实践,通过演讲的形式表达出来,能够和共事,或者本人自问自答。

退出移动版