口诀:成员变量,静态方法看左边;非静态方法:编译看左边,运行看右边
当父类变量引用子类对象时(
Father f = new Son();
),在这个引用变量f
指向的对象中,它的成员变量和静态方法与父类是一致的,他的非静态方法,在编译时是与父类一致的,运行时却与子类一致(发生了复写)
1. 什么是多态
多态是指允许不同子类型的对象对同一行为作出不同的响应。例如在生活中,比如跑的动作,小猫、小狗和大象,跑起来是不一样的。再比如飞的动作,昆虫、鸟类和飞机,飞起来也 是不一样的。可见,同一行为,通过不同的事物,可以体现出来的不同的形态。多态描述的就是这样的状态。
多态性分为编译时的多态性和运行时的多态性。方法重载(overload)实现的是编译时的多态性(也称为前绑定),而方法重写(override)实现的是运行时的多态性(也称为后绑定)。运行时的多态是面向对象最精髓的东西,要实现运行时多态需要做以下两件事情:
- 方法重写(子类继承父类并重写父类中已有的或抽象的方法)
- 用父类型引用变量引用子类型对象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为
如果在编译时能够确定执行多态方法中的哪一个,称为编译时多态,否则称为运行时多态。
2. 编译时多态
-
方法的重载
方法重载就是在同一个类中,出现了多个同名的方法,他们的参数列表(方法签名)不同 (参数列表的个数不同,参数列表的数据类型不同,参数列表的顺序不同)
根据实际参数的数据类型、个数和次序,Java在编译时能够确定执行重载方法中的哪一个。
-
方法重写时的编译时多态:
除了重载,重写也表现出两种多态性。当一个对象的引用指向的是当前对象所属类的对象时,为编译时多态,其他则为运行时多态。
3. 运行时多态
当父类引用指向子类对象时:
-
父类只能执行那些在父类中声明、被子类覆盖了的子类方法,而不能执行子类新增加的成员方法。在编译时期,首先会去查看父类里面有没有这个方法,如果没有的话向上继续查找,直到找到
Object
类如果还没有的话就报错,如果有的话,到运行阶段,再去看一下子类中有没有覆盖该方法,如果覆盖了,则执行子类覆盖的方法。如果没有则执行父类中原本的方法。 -
当子类和父类有相同属性时,父类还是会执行自己所拥有的属性,若父类中没有的属性子类中有,当父类对象指向子类引用时(向上转型),在编译时期就会报错
-
对于
static
方法还是会执行父类中的方法,这是由于在运行时,虚拟机已经认定static
方法属于哪个类。“重写”只能适用于实例方法,不能用于静态方法。**对于静态方法,只能隐藏,重载,继承。**子类会将父类静态方法的隐藏(hide),但子类的静态方法完全体现不了多态,就像子类属性隐藏父类属性一样,在利用引用访问对象的属性或静态方法时,是引用类型决定了实际上访问的是哪个属性,而非当前引用实际代表的是哪个类。因此,子类静态方法不能覆盖父类的静态方法
/**
* @author yzz
* @create 2021-04-17 19:57
*/
public class PolymorphismTest {
public static void main(String[] args) {
// 运行时多态
SuperClass clazz = new SubClass();
// 执行的是子类里的方法
clazz.method();
// 执行的是父类中的方法
clazz.method2();
// 子类新增的方法:报错
// clazz.method1();
// 相同属性:调用父类的属性值
System.out.println(clazz.str);
// 子类新增属性:报错
// System.out.println(clazz.sonInt);
// 静态方法:执行父类中的方法
clazz.method3();
}
}
// 父类
class SuperClass {
static int superInt = 1;
String str = "father";
public SuperClass() {
System.out.println("父类的构造方法");
}
public void method() {
System.out.println("父类的method()");
}
public void method2() {
System.out.println("父类method2()");
}
public static void method3() {
System.out.println("父类static method3()");
}
}
// 子类
class SubClass extends SuperClass {
static int superInt = 2;
String str = "Son";
int sonInt = 1;
public SubClass() {
System.out.println("子类的构造方法");
}
@Override
public void method() {
System.out.println("子类的method()");
}
public void method1() {
System.out.println("子类新增方法method1");
}
public static void method3() {
System.out.println("子类static method3()");
}
}
输出:
父类的构造方法
子类的构造方法
子类的method()
父类method2()
father
父类static method3()
总结
- 多态是指不同子类型的对象对同一行为作出不同的响应。
- 多态性分为编译时的多态性和运行时的多态性。方法重载实现的是编译时的多态性,而方法重写实现的是运行时的多态性。
- 对于运行时多态,特别注意,父类引用指向子类对象,在调用实例方法时,调用的是子类重写之后的,并且不能调用子类新增的方法,对于属性和static方法来说,还是执行父类原有的