面试题:重载(Overload)和重写(Override)的区别。能否根据返回类型来区分重载方法?面试官调查点猜测,这道题纯粹是考基础理论知识,对实际开发工作没有太大的指导意义。毕竟编辑器有语法提示功能。如果不写Correct,就会出现错误信息。背景知识详解关于重载(Overload)和重写(Override),它们在实际开发中使用频率很高,涉及的背景知识也不难。重写重写是子类重写父类允许访问的方法的实现过程,返回值和形参都不能改变。即外壳不变,核心重写!重写发生在类的继承关系中,或者类的实现关系中。改写后的方法和原来的方法需要保持完全相同的返回值类型、方法名、参数个数、参数类型。简单的说就是子类重写的方法必须和父类完全一致。类的继承关系我们根据继承关系来看下面的例子。classAnimal{publicvoidmove(){System.out.println("动物可以移动");}}classBirdextendsAnimal{publicvoidmove(){System.out.println("鸟会飞");}}classDogextendsAnimal{publicvoidmove(){System.out.println("Dogscanrun")}}publicclassTestMain{publicstaticvoidmain(Stringargs[]){Animala=newAnimal();//动物对象Animalb=newBird();//鸟对象Animalc=newDog();//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=newDog();//Dog对象c.move();//执行Dog类的方法有些情况下,子类重写了父类的方法,我们希望调用子类重写后的方法,同时它仍然可以调用父类重写的方法,如何实现?super关键字当需要在子类中调用父类重写的方法时,使用super关键字。classAnimal{publicvoidmove(){System.out.println("动物可以移动");}}classBirdextendsAnimal{publicvoidmove(){super.move();//添加超级调用System.out。println("鸟会飞");}}}publicclassTestMain{publicstaticvoidmain(Stringargs[]){Animalb=newBird();//Bird对象b.move();//执行Bird类的方法}}运行结果如下:动物能动,鸟能飞方法重写规则总结一下,在Java中,方法重写的规则。参数列表必须与重写方法的参数列表相同。返回类型可以和重写方法的返回类型不同,但必须是父类返回值的派生类(java5及之前版本的返回类型必须相同,java7及以后版本可以有所不同)。访问权限不能低于父类中重写方法的访问权限。例如:如果父类的一个方法声明为public,那么重写子类中的方法就不能声明为protected。超类的成员方法只能被其子类覆盖。不能重写声明为final的方法。声明为静态的方法不能被覆盖,但可以重新声明。如果子类和父类在同一个包中,那么子类可以覆盖父类的所有方法,除了声明为private和final的方法。如果子类和父类不在同一个包中,则子类只能覆盖父类声明为public和protected的非final方法。被覆盖的方法可以抛出任何非强制异常,无论被覆盖的方法是否抛出异常。但是,被覆盖的方法不能抛出新的强制性异常,或者比被覆盖的方法声明的范围更宽的强制性异常,反之亦然。构造函数不能被覆盖。如果你不能继承一个类,你就不能覆盖那个类的方法。基于接口实现的重写基于接口实现的重写在实际应用中使用非常频繁。以线程实现为例,如图所示,代表了Thread和Runnable的类关系图。Runnable是一个接口,它定义了线程的执行方法,代码如下:@FunctionalInterfacepublicinterfaceRunnable{/***当一个实现接口Runnable
的对象被*创建线程时,启动thread导致对象的*run
方法在单独执行的*线程中被调用。*
*run
方法的一般约定是它可以*采取任何行动。**@seejava.lang.Thread#run()*/publicabstractvoidrun();}在实际应用中,我们可以直接继承这个接口来声明一个线程。Thread是一个普通的线程类,实现了Runnable接口,重写了Runnable接口的run方法。这里这样设计的目的是为了避免Java中一个类只能实现一个接口的规则。如果一个类继承了其他接口,但又想实现线程时的问题。publicclassThreadimplementsRunnable{@Overridepublicvoidrun(){if(target!=null){target.run();}}}由于接口只是用于规范设计,用来描述一个对象有什么行为,但是没有具体的实现,所以如果需要声明线程,需要实现接口,重写其中的抽象方法(没有在接口中实现的方法是抽象的,必须重写子类)。Thread类中重写了Runnable中的run方法,调用了target.run()。这个target才是真正的线程业务实现,而Thread只是一种委托设计模式。因此,如果我们想通过继承Thread来实现线程,就需要编写如下代码来实现,其中target是代表子类的App的对象实例。publicclassAppextendsThread{@Overridepublicvoidrun(){//doSomething}}由于接口只是行为规范,本身不提供实现,所以实现接口的子类必须“必须”重写父类的方法class,这与类继承不同。重载(overloading)在一个类中,方法名相同,只是参数不同。返回类型可以相同或不同。每个重载方法(或构造函数)都必须有一个唯一的参数类型列表。最常用的地方就是构造函数的重载。比如在ThreadPoolExecutor线程池的实现类中,可以看到如下的重载方法。publicThreadPoolExecutor(intcorePoolSize,intmaximumPoolSize,longkeepAliveTime,TimeUnitunit,BlockingQueue