面试题:重载(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()。表示动物的具有的行为。

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

重写的好处在于子类可以根据需要,定义特定于自己的行为。也就是说子类能够根据需要实现父类的方法。

在类继承关系中,父类的非抽象方法,子类是不强制要求重写的。在实际应用中,如果重写了父类的方法,并且实例对象的引用指向的是子类时,JVM会自动调用子类重写的方法,此时,父类的方法完全被屏蔽了。就像前面测试的代码。

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

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. Oracle体系结构一

    总体结构分为三个部分:SGA,PGA,FILE文件 按功能分: 存储结构  存储结构对应关系  主要文件: 数据文件: 每个数据文件只与一个数据库相关联 一个表空间可以包含一个或者多个数据文件 一个数 ...

  2. Python爬无止境,获得王者荣耀全部高清皮肤

    作为一名热爱王者两年的程序员,早就想爬取所有英雄皮肤的高清照片,在设个幻灯片放映,真香,这次只用16行代码就能实现,对于新手拿这个作为实战练手项目既简单又容易上手,快来尝试下. 百度"王者荣 ...

  3. PHP中的输出:echo、print、printf、sprintf、print_r和var_dump

    大家在面试中,经常会被问到的问题: 请简要说明PHP的打印方式都有哪些? 或者直接点问: 请说明echo.print.print_r的区别 看着很简单,一般会出现在初中级的笔试题中.但是要真正说明白这 ...

  4. JDK1.8源码(三)——java.lang.String类

    一.概述 1.介绍 String是一个final类,不可被继承,代表不可变的字符序列,是一个类类型的变量.Java程序中的所有字符串字面量(如"abc")都作为此类的实例实现,&q ...

  5. 第三方api接口

    做为一个软件测试工程师,你要学习接口测试,需要练习,那么就要有调用的api,可以参考以下的文章. 国内7款API供应平台功能对比及详细介绍 https://blog.csdn.net/ishxiao/ ...

  6. Redis-Cluster分片扩容

    redis分片分片场景在业务量相对较小的时候,可以将所有数据都存到一台机器上,只使用redis单机模式,不存在分片问题.如果业务的数据量超过一台物理机器的内存大小时,则会面对扩展问题,需要多台机器去存 ...

  7. JAVA-java内存分配

    二.java-class的内存分配 三.JAVA string类特别之处 String 通过构造方法创建是在堆内存中, 通过直接赋值对象是在方法区的常量里 四.字符串做拼接 非常耗时和浪费内存的原因 ...

  8. Viterbi 算法 Python实现 [NLP学习一]

    最近思考了一下未来,结合老师的意见,还是决定挑一个方向开始研究了,虽然个人更喜欢鼓捣.深思熟虑后,结合自己的兴趣点,选择了NLP方向,感觉比纯粹的人工智能.大数据之类的方向有趣多了,个人还是不适合纯粹 ...

  9. Linux如何配置网络ip?

    Linux如何配置网络ip? 1.首先切换至root用户 su root 输入root用户密码 2.借助dhclient工具自动生成一个网络内可用的ip地址 我们可以手动配置对应的网段的ip地址,但是 ...

  10. Linux命令行:监视系统IO、内存、CPU、GPU

    [监视IO] Linux18.04自带工具sysstat,其中的iostat可以用于观察IO情况.Linux16.04系统没有默认安装,需要手动安装,安装sysstat时需要下载新的内核(我的原本是1 ...