面试题:重载(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),并不会去声明返回值,由于这种情况的存在,所以这个理论也不能实现。

函数的返回值只是作为函数运行之后的一个“状态”他是保持方法的调用者与被调用者进行通信的关键。并不能作为某个方法的“标识”

问题总结

这个问题,其实是属于那种,你不问我,我一定会认为自己知道,而且在工作开发中也能使用不会出问题,但是你一问我,我一定会懵逼,不是因为真的不懂,而是不知道怎么去组织语言来描述这两个概念。

建议大家参考“费曼学习法”,就是把这篇文章学到的理论,通过演讲的方式表达出来,可以和同事,或者自己自问自答。

大厂面试题系列:重载(Overload)和重写(Override)的区别。重载的方法能否根据返回类型进行区分的更多相关文章

  1. 抽象方法(abstract method) 和 虚方法 (virtual method), 重载(overload) 和 重写(override)的区别于联系

    1. 抽象方法 (abstract method) 在抽象类中,可以存在没有实现的方法,只是该方法必须声明为abstract抽象方法. 在继承此抽象类的类中,通过给方法加上override关键字来实现 ...

  2. Java面试 - 重载(Overload)和重写(Override)的区别?

    1.重载是在同一个类中,可声明多个同名方法,但参数列表不同(参数顺序,个数,类型).而重写是在子类中,对从父类中继承的方法进行重新编写,但方法名,参数列表(参数顺序,个数,类型),返回值类型必须保持一 ...

  3. 【Java】重载(Overload)与重写(Override)

    方法的语法 修饰符 返回值类型 方法名(参数类型 参数名){ ... 方法体 ... return 返回值; } 重载(overload) /** * 重载Overload: * 同一个类中,多个方法 ...

  4. Java方法多态性——方法的重载Overload和重写Override

    方法的重写(Overriding)和重载(Overloading)是java多态性的不同表现,重写是父类与子类之间多态性的一种表现,重载可以理解成多态的具体表现形式. 重写(Override) 重写是 ...

  5. 重载(Overload)和重写(Override)的区别。重载的方法能否根据返回类型进行区分?

    方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性.重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同.参数个数不同或者二者都不同)则视 ...

  6. 重载(Overload)和重写(Override)的区别。重载的 方法能否根据返回类型进行区分?

    方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性.重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同.参数个数不同或者二者都不同)则视 ...

  7. 重载overload 、重写override

    观点:重载和重写完全没有关系要联系到一起,唯一的联系就是他们都带有个'重'字,所以鄙人也随大流把他们放在了一起 注意:下面可复制的代码是正确的,错误的只会上传图片,不上传可复制的代码 重载 1.在同一 ...

  8. C++ 重载(overload)、重写(overrride)、重定义(redefine)总结

    引自:http://www.189works.com/article-42111-1.html 先来看几个概念: 重载(overload),重写(override,也称覆盖), 重定义(redefin ...

  9. 方法的覆盖(override)、重载(overload)和重写(overwrite)

    body { background-color: white } .markdown-body { min-width: 200px; max-width: 760px; margin: 0 auto ...

随机推荐

  1. docker&flask快速构建服务接口(二)

    系列其他内容 docker快速创建轻量级的可移植的容器✓ docker&flask快速构建服务接口✓ docker&uwsgi高性能WSGI服务器生产部署必备 docker&g ...

  2. ldconfig与 /etc/ld.so.conf

    现在我们知道了动态与静态函数库,也知道了当前的Linux大多是将函数库做成动态函数库,下面来讨论增加函数库读取性能的方法.我们知道,内存的访问速度是硬盘的好几倍,所以,如果将常用的动态函数库加载到内存 ...

  3. python类、对象

    python类.对象 学习完本篇,你将会深入掌握 什么是类,对象 了解类和对象之间的关系 能独立创建一个People(人类),属性有姓名.性别.年龄.提供一个有参的构造方法,编写一个show方法,输出 ...

  4. 学习PHP中有趣的字符集国际化验证功能

    今天的内容非常简单,不过也很有趣.不知道大家有没有经历过这样的事情,就是在某些字体下,0 和 O 不好区分,1 和 l 也是很难看清楚.当然,现在大部分的编辑器和 IDE 的默认字体都是会选择那些比较 ...

  5. PHP出现iconv(): Detected an illegal character in input string

    PHP传给JS字符串用ecsape转换加到url里,又用PHP接收,再用网上找的unscape函数转换一下,这样得到的字符串是UTF-8的,但我需要的是GB2312,于是用iconv转换 开始是这样用 ...

  6. 微信公众号jssdk分享接口onMenuShareAppMessage自定义的参数无效,微信分享失败原因

    使用jssdk为jweixin-1.4.0.js updateTimelineShareData,安全域名接口也设置正确,可就是分享,转发朋友圈不成功. 解决方案:采用<script src=& ...

  7. 为什么Charles中的中文展示成数字、英文字符串

    在使用charles抓包时,可能非看到如下图的字符串: 为什么会出现这样的字符串? 我们看到的汉字.字母,对电脑来说并不长这样,而是用二进制表示的(显然--),为了统一标准,老外发明了"字符 ...

  8. phpspider PHP 爬虫

    * 通过composer下载 composer require owner888/phpspider // composer.json { "require": { "o ...

  9. python之jsonpath

    json 官方文档:http://docs.python.org/library/json.html JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,它使 ...

  10. 鸿蒙内核源码分析(文件系统篇) | 用图书管理说文件系统 | 百篇博客分析OpenHarmony源码 | v63.01

    百篇博客系列篇.本篇为: v63.xx 鸿蒙内核源码分析(文件系统篇) | 用图书管理说文件系统 | 51.c.h.o 文件系统相关篇为: v62.xx 鸿蒙内核源码分析(文件概念篇) | 为什么说一 ...