参考文献
Java 重写(Override)与重载(Overload)
子类重载父类方法
重载
重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
最常用的地方就是构造器的重载。
细说一下重写和重载:
这两个都是多态的一种表现形式。
重载:
1、 重载是在编译器通过方法中形参的静态类型确定调用方法版本的过程。
2、 重载是多态在编译期的表现形式
3、 重载的判定只有两个条件(其他的条件都不能作为判定):
1、 方法名一致
2、形参列表不同
重写:
1、重写在方法运行时,通过调用者的实际类型来确定调用的方法版本。(具体细说,就是子父类中的重写方法在对应的class文件常量池的位置相同,一旦子类没有重写,那么子类的实例就会沿着这个位置往上找,直到找到父类的同名方法)
2、重写只发生在可见的实例方法中:
1、静态方法不存在重写,形式上的重写只能说是隐藏。
2、私有方法也不存在重写,父类中private的方法,子类中就算定义了,就是相当于一个新的方法。
3、静态方法和实例方法不存在相互重写。
3、重写满足一个规则:两同两小一大
1、两同:方法名和形参列表一致
2、两小:重写方法的返回值(引用类型)和抛出异常,要和被重写方法的返回值(引用类型)和抛出异常相同或者是其子类。注意,一旦返回值是基本数据类型,那么重写方法和被重写方法必须相同,且不存在自动拆装箱的问题。
3、一大:重写方法的访问修饰符大于等于被重写方法的访问修饰符。
重载的概念是:
方法名称相同,参数个数、次序、类型不同
因此重载对返回值没有要求,可以相同,也可以不同
但是如果参数的个数、类型、次序都相同,方法名也相同,仅返回值不同,则无法构成重载。
重载是Java多态性(在一个类中)的一种表现方式,要注意以下几点:
1.参数列表不同。包括参数个数、参数类型、参数顺序等的不同;
2.不能通过方法的访问权限、返回值类型和抛出的异常类型来判断重载;
3.对于继承来说,如果父类方法的访问权限为private,那么就不能在子类对其重载;如果子类也定义了一个同名的函数,只是一个新方法,并不是重载方法。
首先,重载和重写都是多态的一种体现方式。重载是编译期间的活动,重写是运行期间的活动。
其次,重载是在一个类中定义相同的名字的方法,方法的参数列表或者类型要互相不同,但是返回值类型不作为是否重载的标准,可以修改可见性;
重写是不同的,要求子类重写基类的方法时要与父类方法具有相同的参数类型和返回值,可见性需要大于等于基类的方法
class UseAnimal{
void doStuff(Animal sa){
}
void doStuff(Tiger sa){
}
public static void main(String[] args){
UseAnimal ua=new UseAnimal();
Animal an=new Tiger();
ua.duStuff(an);
}
}
此时,调用的方法doStuff的Animal版本,因为调用重载方法是在编译时决定的,an的声明类型是Animal。所以调用Animal版本。
始终注意一点:重载的判断始终是在编译时决定
重写
重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。
重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常。例如: 父类的一个方法申明了一个检查异常 IOException,但是在重写这个方法的时候不能抛出 Exception 异常,因为 Exception 是 IOException 的父类,只能抛出 IOException 的子类异常。
重写必须要有继承关系
方法重写
只有实例方法才能被重写,超类中的final方法不能被重写。
尽管多态是在编译时确定对象的类型,但在编译时,还是根据父类的方法声明进行程序检查。因此,如果子类中定义的方法,在父类中没有定义,则会出项编译错误。
参数列表必须完全与被重写方法的相同;
返回类型必须完全与被重写方法的返回类型相同;
访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为public,那么在子类中重写
该方法就不能声明为protected。
父类的成员方法只能被它的子类重写。
声明为final的方法不能被重写。
声明为static的方法不能被重写,但是能够被再次声明。
子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为private和final的方法。
子类和父类不在同一个包中,那么子类只能够重写父类的声明为public和protected的非final方法。
重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异
常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
构造方法不能被重写。
如果不能继承一个方法,则不能重写这个方法。
方法重写要注意的事项:
1.方法重写时, 方法名与形参列表必须一致。
2.方法重写时,子类的权限修饰符必须要大于或者等于父类的权限修饰符。
3.方法重写时,子类的返回值类型必须要小于或者等于父类的返回值类型。
4.方法重写时, 子类抛出的异常类型要小于或者等于父类抛出的异常类型。
Exception(大)
RuntimeException(小)
方法重载
被重载的方法必须改变参数列表(参数个数或类型或顺序不一样);
被重载的方法可以改变返回类型;
被重载的方法可以改变访问修饰符;
被重载的方法可以声明新的或更广的检查异常;
方法能够在同一个类中或者在一个子类中被重载。
无法以返回值类型作为重载函数的区分标准。
class Animal{
public void move(){
System.out.println("动物可以移动");
}
}
class Dog extends Animal{
public void move(){
System.out.println("狗可以跑和走");
}
}
public class TestDog{
public static void main(String args[]){
Animal a = new Animal(); // Animal 对象
Animal b = new Dog(); // Dog 对象
a.move();// 执行 Animal 类的方法
b.move();//执行 Dog 类的方法
}
}
动物可以移动
狗可以跑和走
在上面的例子中可以看到,尽管 b 属于 Animal 类型,但是它运行的是 Dog 类的 move方法。
这是由于在编译阶段,只是检查参数的引用类型。然而在运行时,Java 虚拟机(JVM)指定对象的类型并且运行该对象的方法。因此在上面的例子中,之所以能编译成功,是因为 Animal 类中存在 move 方法,然而运行时,运行的是特定对象的方法。编译看左边,运行看右边
Super 关键字的使用
当需要在子类中调用父类的被重写方法时,要使用 super 关键字
重载与重写区别总结
重载 | 重写 | |
---|---|---|
多态的表现形式 | 编译期间的活动 | 运行期间的活动 |
父类private修饰的方法 | 否 | 否 |
(static)静态方法 | 可以 | 不可以 |
final方法 | 可以 | 不可以 |
跨包,修饰符缺省的方法 | 可以 | 不可以 |
抛出异常 | 可以声明新的或更广的检查异常 | 不能比原方法的大 |
返回类型 | 无法以返回值类型作为重载函数的区分标准 | 与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类 |
访问权限 | 被重载的方法可以改变访问修饰符 | 访问权限不能比父类中被重写的方法的访问权限更低 |
参数列表 | 必须修改 | 不能修改 |
private是私有的,其他类是不能访问的,子类也不可以访问,所以你可以重新实现父类的该方法,不会有冲突,但是你重新实现的方法,不叫重写也不叫重载,是一个该子类新增的方法,和子类的一般扩展方法一样
在java中,private的变量和方法对在类外是不可见的,当然更谈不上重写
Java中方法不能重载的三种情况
1.对于两个方法,如果只有返回值不同,那么不构成重载,程序会报错。
2.对于两个方法,如果只有访问修饰符不同,那么不构成重载,程序会报错。
3.对于两个方法,如果只是参数命名不同,那么不构成重载,程序会报错。
总结:重载关心的只是参数,有参无参,参数类型不同,参数数量不同,不同类型的参数顺序不同,都可以实现方法的重载。
在Java中,static方法和final方法(private方法属于final方法)是前期绑定,而其他所有的方法都是后期绑定,所以可以被重载,不能被重写
子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
构造方法不能被重写
如果不能继承一个类,则不能重写该类的方法
静态方法为什么不能被重写
重写依赖于拥有类的实例。多态性的要点是可以对类进行子类化,实现这些子类的对象对于在超类中定义的相同方法(在子类中被重写)具有不同的行为。静态方法不与类的任何实例相关联,因此该概念不适用
静态方法属于类,是不能被重写,故而也不能实现多态
其实一个指向子类对象的父类引用变量来调用父子同名的静态方法时,只会调用父类的静态方法。这是因为静态方法只能被继承,不能被重写,如果子类有和父类相同的静态方法,那么父类的静态方法将会被隐藏,对于子类不可见,也就是说,子类和父类中相同的静态方法是没有关系的方法,他们的行为不具有多态性。但是父类的静态方法可以通过父类.方法名调用。
重载是实现多态的一种方法,方法名相同,参数列表不同。重写是子类覆盖父类的方法,方法名相同参数相同而方法体不同。在此可以重载而不同重写。
继承是用于描述类与类之间关系的,方法不能叫继承而叫重写或覆盖或复写,final修饰的类不能被继承,final修饰的方法不能被重写
方法的重写(Overriding)和重载(Overloading)是java多态性的不同表现,重写是父类与子类之间多态性的一种表现,重载可以理解成多态的具体表现形式。
(1)方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载(Overloading)。
(2)方法重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写(Overriding)。
(3)方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。
public class A {
private void print() {
System.out.println("A");
}
public static void main(String[] args) {
A a = new B();
a.print();
B b = new B();
b.print();
new B().print();
}
}
class B extends A {
public void print() {
System.out.println("B");
}
}
结果是
A
B
B
回到这个代码,父类中的方法是private的,即子类是“看不到”的,所以也就没有被“copy”到子类中,所以看似是重写,实际上是一个属于子类的新的方法,只不过“恰好”和父类中的方法重名了。
再想多态的要求,子类自己新增的方法在多态的情况下是不能使用的,是不可见的,所以当调用print方法时,编译器看到子类没有父类中的那个方法或者其重写,就会选择调用父类的方法。如果将父类的private改为高一级的可视性,子类就可以“看到”了,也就“copy”过来了,输出为
B
B
B
构造函数
如果类中有跟类同名的方法,但是有方法修饰符修饰,那么就不是构造函数了,而是一个普通的方法
构造函数不能被继承,构造方法只能被显式或隐式的调用。
类中的构造方法可以省略不写的
构造方法必须跟类名相同,普通的类方法能与类同名的,但是要返回一个值。
构造方法都在new 对象的时候调用的
一个类可以定义多个构造方法的
构造方法是一种特殊的方法,具有以下特点。
(1)构造方法的方法名必须与类名相同。
(2)构造方法没有返回类型,也不能定义为void,在方法名前面不声明方法类型。
(3)构造方法的主要作用是完成对象的初始化工作,它能够把定义对象时的参数传给对象的域。
(4)一个类可以定义多个构造方法,如果在定义类时没有定义构造方法,则编译系统会自动插入一个无参数的默认构造器,这个构造器不执行任何代码。
(5)构造方法可以重载,以参数的个数,类型,顺序。
如果有多个类,多个类都可以有自己的构造器
java继承中对构造函数是不继承的,只是显式或者隐式调用
构造器(构造方法)是否可以被重写
构造函数不能被继承
派生类不能继承基类的构造函数,必需提供自个的构造函数,防止紧耦合
构造方法是用来初始化类的对象,与父类的其他成员不同,他不能被子类继承(子类可以继承父类所有的成员变量和成员方法,但是不继承父类的构造方法)。因此,在创建子类对象时,为了初始化从父类继承来的数据成员,系统需要调用其父类的构造方法。
如果没有显式的构造函数,编译器会给一个默认的构造函数,并且该默认的构造函数仅仅在没有显式的声明构造函数情况下创建。
构造原则如下:
如果子类没有定义构造方法,则调用父类的无参数构造方法
如果子类定义了构造方法,不论无参数还是带参数,在创建子类的对象的时候,首先执行父类无参数的构造方法,然后执行自己的构造方法。
在创建子类对象的时候,如果子类的构造函数没有显式调用父类的构造函数,则会调用父类的默认无参构造函数。
在创建子类对象时候,如果子类的构造函数没有显式调用父类的构造函数,且父类自己提供了无参构造函数,则会调用父类自己的无参构造函数。
在创建子类对象时候,如果子类的构造函数没有显示调用父类的构造函数且父类之定义了自己的有参构造函数,则会出错(如果父类只有有参数的构造方法,则子类必须显示调用此带参构造方法)。
如果子类调用父类带参数的构造方法,需要初始化父类成员对象的方法。
注意:子类只能在构造对象时才能默认(或者用初始化列表中显式调用“特定父类构造函数”)调用父类的构造函数,在构造完成后不能像调用父类成员函数一样调用父类构造函数,这样保证了父类构造函数只调用一次的原则。
构造函数不能被重写
构造器是不能被继承的,因为每个类的类名都不相同,而构造器名称与类名相同,所以根本谈不上继承。 又由于构造器不能继承,所以就不能被重写。但是,在同一个类中,构造器是可以被重载的。
重写的定义是方法名称相同,方法参数列表,返回值也要相同,只有继承了父类,子类才可以重写,但是子类的名称和父类的名称是不能一样的,从而构造函数的名字也是不一样的,所以就谈不上重写了
构造函数只能被重载,不能被重写
多态
对于同一个父类方法,不同的子类会有不同的实现方式
《疯狂java讲义》的标准解释是: 相同类型的变量、调用同一个方法时呈现出多种不同的行为特征,这就是多态
针对一消息,不同的对象可以以适合自身的方式加以响应
对于多态,可以总结它为:
一、使用父类类型的引用指向子类的对象;
二、该引用只能调用父类中定义的方法和变量;
三、如果子类中重写了父类中的一个方法,那么在调用这个方法的时候,将会调用子类中的这个方法;(动态连接、动态调用)
四、变量不能被重写(覆盖),”重写“的概念只针对方法,如果在子类中”重写“了父类中的变量,那么在编译时会报错。
多态的3个必要条件:
1.继承 2.重写 3.父类引用指向子类对象。
向上转型: Person p = new Man() ; //向上转型不需要强制类型转化
向下转型: Man man = (Man)new Person() ; //必须强制类型转化
1 观察以下代码: 下列哪些针对代码运行结果的描述是正确的?A
class Car extends Vehicle
{
public static void main (String[] args)
{
new Car(). run();
}
private final void run()
{
System. out. println ("Car");
}
}
class Vehicle
{
private final void run()
{
System. out. println("Vehicle");
}
}
Car
Vehicle
Compiler error at line 3
Compiler error at line 5
Exception thrown at runtime
首先final声明的方法是不能被覆盖的,但是这里并不错误,因为方法是private的,也就是子类没有继承父类的run方法,因此子类的run方法跟父类的run方法无关,并不是覆盖。new Car().run()也是调用子类的run方法。
此题的父类方法有private修饰,所以对子类不可见,子类不能覆盖。所以子类方法和父类是两个方法。
扩展:如果父类方法将private改为public 会怎样?会报错,因为父类方法有final修饰,不能被覆盖。
2 根据以下代码段,下列说法中正确的是( C )。
public class Parent {
private void m1(){
}
void m2(){
}
protected void m3(){
}
public static void m4(){
}
}
子类中一定能够继承和覆盖Parent类的m1方法
子类中一定能够继承和覆盖Parent类的m2方法
子类中一定能够继承和覆盖Parent类的m3方法
子类中一定能够继承和覆盖Parent类的m4方法
通过继承,子类可以拥有所有父类对其可见的方法和域
A.私有方法只能在本类中可见,故不能继承,A错误
B.缺省访问修饰符只在本包中可见,在外包中不可见,B错误
C.保护修饰符凡是继承自该类的子类都能访问,当然可被继承覆盖;C正确
D.static修饰的成员属于类成员,父类字段或方法只能被子类同名字段或方法遮蔽,不能被继承覆盖,D错误
public和protected都可以作用于子类,但在多态情况下,静态函数调用时编译和运行看左边,所以子父类存在同名静态函数访问的是父类,子类并不能覆盖父类的方法,所以选C
子类重写父类的静态方法,被子类调用时,子类方法并不会生效,而是调用了父类的静态方法。static关键字至关重要,经常在选项中出现,只要记住static属于类,在编译阶段就已经确定函数名和地址,并已经分配了内存,所以不存在运行时期多态的判断,他只是一个静态绑定,所以无法重写。
Java中static静态方法可以继承吗?可以被重写吗?
结论:可以被继承,但是不能被重写,如果父子类静态方法名相同,则会隐藏父类方法。
1、Java中所有方法都能被继承,包括私有方法(但不可见)和静态方法。
2、Father f = new Son(); f.staticMethod();
Father f = null; f.staticMethod();
这两个都会调用父类的方法,因为静态方法是类的方法和具体对象没关系,和直接用类名调用一样。
3、静态方法是编译时绑定的,方法重写是运行时绑定的。