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

面试问题:超负荷和超负荷的区别。重载的方法可以根据返回类型来区分吗?

面试官考察点猜想#

这个问题纯粹是对基础理论知识的考查,对实际开发工作的指导意义不大。毕竟编辑器都有语法提示功能,写的不对就会有错误提示。

背景知识详解#

实际开发中经常用到重载和重写,涉及的背景知识并不难。

重写#

重写子类是重写父类的可访问方法的实现过程,返回值和参数不可更改。就是外壳不变,内核重写!

重写发生在类的继承关系或者类的实现关系中。重写后的方法和原方法需要保持相同的返回值类型、方法名、参数个数和参数类型。简而言之,子类重写的方法必须与父类完全一致

类的继承关系#

让我们看下面这个基于继承关系的例子。

class Animal { public void move(){ system . out . println(& # 34;动物可以移动& # 34;); } } 类鸟扩展动物{ public void move(){ system . out . println(& # 34;鸟儿会飞& # 34;); } } 类狗扩展动物{ public void move(){ system . out . println(& # 34;狗会跑& # 34;) } } 公共类TestMain{ 公共静态void main(String args[]){ Animal a = new Animal();//Animal object 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()方法。

动物c =新狗();// Dog对象 c . move();//执行Dog类的方法 在某些情况下,子类会重写父类的方法。我们希望在调用子类来覆盖方法的同时,我们仍然可以调用父类被覆盖的方法。我们如何做到这一点?

Super关键字#

当需要调用子类中父类的重写方法时,使用super关键字。

class Animal { public void move(){ system . out . println(& # 34;动物可以移动& # 34;); } } 类鸟延伸动物{ public void move(){ super . move();//添加超级调用 system . out . println(& # 34;鸟儿会飞& # 34;); } } } 公共类TestMain{ 公共静态void main(String args[]){ Animal b = new Bird();//Bird object b . move();//执行Bird类 } } 的方法。运行结果如下:

动物会动 鸟会飞 重写方法的规则#总结一下,在Java中,方法重写的规则。

  • 参数列表必须与被重写的方法完全相同。
  • 返回类型可以不同于被覆盖方法的返回类型,但必须是父类返回值的派生类(java5和更早版本的返回类型相同,java7和更高版本可以不同)。
  • 访问权限不能低于父类中被重写方法的访问权限。例如,如果父类的方法被声明为public,那么如果它在子类中被重写,就不能被声明为protected。
  • 父类的成员方法只能被它的子类重写。
  • 不能重写声明为final的方法。
  • 声明为static的方法不能被重写,但可以再次声明。
  • 如果子类和父类在同一个包中,子类可以覆盖父类的所有方法,除了那些声明为private和final的方法。
  • 如果子类和父类不是同一个包的final,那么子类只能重写声明为public并受父类保护的非final方法。
  • 被重写的方法可以引发任何非强制异常,不管被重写的方法是否引发异常。但是,被重写的方法不能引发新的强制异常或比被重写的方法声明的强制异常更广泛的强制异常,反之亦然。
  • 该方法不能被重写。
  • 如果不能继承某个类,则不能重写该类的方法。
  • 基于接口实现的重写#

    基于接口的重写在实际应用中经常使用。以线程实现为例,如图,展示了Thread和Runnable的类图。



    Runnable是一个定义线程执行方法的接口。代码如下:

    @ functional interface public interface Runnable { /* * *当对象实现接口& lt代码& gtRunnable & lt/code & gt;用于 *创建一个线程,启动线程会导致对象& # 39;s * & lt;代码& gt运行& lt/code & gt;要在单独执行的 *线程中调用的方法。 * & lt;p & gt *方法的一般合同& lt代码& gt运行& lt/code & gt;它可以 *采取任何行动。 * * @ see Java . lang . thread # run() */ 公共抽象void run(); } 在实际应用中,我们可以直接继承这个接口来声明一个线程。

    Thread是一个普通的线程类,实现了Runnable接口,并覆盖了Runnable接口的run方法。这种设计的目的是为了避免Java中的一个类只能实现一个接口的规则所导致的问题,如果一个类继承了其他接口但想要实现线程的话。

    public 类线程实现Runnable { @ Override public void run(){ if(target!= null){ target . run(); } } } 因为接口只是用于规范设计来描述一个对象的行为,它没有具体的实现,所以如果需要声明一个线程,就需要实现接口,重写其中的抽象方法(接口中没有实现的方法是抽象的,子类必须重写)。

    Thread类覆盖Runnable中的run方法,该方法调用target.run()。这个目标是thread业务的真实实现,Thread只是一个委托设计模式。

    所以如果要通过继承Thread来实现Thread,就需要按照下面的代码来实现,其中target是代表子类的App的对象实例。

    oberlo

    public class app extensions thread { @ override public void run(){ /doing something } } 因为接口只是一个行为规范,本身并不提供实现,所以实现了接口的一个子类。

    重载#

    重载在具有相同方法名和不同参数的类中。返回类型可以相同,也可以不同。

    每个重载方法(或构造函数)必须有一个唯一的参数类型列表。

    最常见的地方就是构造函数的重载。例如,在ThreadPoolExecutor线程池的实现类中,可以看到以下重载方法。

    public thread pool executor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, blocking queue & lt;Runnable & gtwork queue){ } public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, blocking queue & lt;Runnable & gtworkQueue, thread factory thread factory){ } public thread pool executor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, blocking queue & lt;Runnable & gtWorkqueue, ThreadFactory, RejectedexecutionHandler Handler){ } 重载方法的好处是类可以统一处理不同类型的手段。调用方法时,传递给它们不同数量和类型的参数来决定使用哪个方法,这就是多态性。

    其特点是:重载发生在这个类中,方法名相同,参数列表不同,与返回值无关,只与方法名和参数类型有关。

    方法重载时,方法之间是有一定联系的,因为这样可以提高程序的可读性,我们通常只重载功能相似的方法。

    重载规则#
  • 重载的方法必须改变参数列表(参数的数量或类型不同);
  • 重载方法可以改变返回类型;
  • 重载方法可以更改访问修饰符;
  • 重载方法可以声明新的或更广泛的检查异常;
  • 方法可以在同一个类或子类中重载。
  • 返回值类型不能作为重载函数的区分标准。
  • 问题解答#

    了解以上知识点后,我们再来看这道面试题。

    面试问题:超负荷和超负荷的区别。重载的方法可以根据返回类型来区分吗?

    差异:

  • 方法重载(Method Overloading)是指一个类中定义的同名方法的重载,但其参数个数不同或相同,只是类型和顺序不同。
  • 方法重写是存在于子类中与父类的方法同名、参数个数和类型相同、返回值相同的方法,称为重写。
  • 方法重载是一个类的多态性,方法重写是子类和父类的多态性。
  • 可以根据返回类型来区分重载方法吗

    重载方法不能按类型区分,只能按参数类型和个数区分。但是对于重载的方法,允许修改返回值类型、异常类型和访问级别,但是不能只根据这些类型的类重载。

    为什么不能只通过返回类型来区分重载?

    原因是在调用目标方法时,不能指定返回值类型信息。这个时候编译器不知道你要调用哪个函数。

    比如下面这段代码,调用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),不声明返回值。因为这种情况,这个理论无法实现。

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

    问题总结#

    这个问题其实属于那种问题。如果你不问我,我肯定会认为我知道,可以用在工作开展上,没有任何问题。但如果你问我,我会很困惑,不是因为我真的不懂,而是因为我不知道如何组织语言来描述这两个概念。

    建议你参考费曼的学习方法,就是通过一次演讲,把本文学到的理论表达出来,和同事或者自己进行问答。

    您可以还会对下面的文章感兴趣

    使用微信扫描二维码后

    点击右上角发送给好友