5.7 多态 5.7.1 多态性

5.7 多态 5.7.1 多态性

引用变量的类型

Java引用变量有两个类型:

  1. 一个是编译时类型,
  2. 一个是运行时类型

编译时类型就是声明该引用变量时使用的类型
运行时类型就是该引用变量所引用的实际对象的类型,一般通过new关键字来创建对象,new关键字后面的类型就是运行时类型.

例如: Person p=new Chinese();这行代码,引用变量p

  • 编译时类型是父类Person,
  • 运行时类型是子类Chinese

如果编译时类型和运行时类型不一致,就可能出现所谓的多态(Polymorphism)。

多态存在的三个必要条件

  1. 继承
  2. 重写
  3. 父类引用指向子类对象

多态方法调用方法时如何确定调用的是哪个方法

当使用多态方式调用方法时,首先检查父类中是否有该方法,

  • 如果父类中没有,则编译错误;
  • 如果父类中有,再去调用子类的重写的同名方法。如果子类没有重写该方法,则调用子类继承得到的方法,也就是父类的方法.

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
class BaseClass {
public int book = 1024;
// public String book = "父类 实例变量";

public void base() {
System.out.println("父类 实例方法 base()");
}

public void test() {
System.out.println("父类 实例方法 test()");
}
}

class A extends BaseClass {
public String book = "子类A 实例变量";

public void test() {
System.out.println("子类A 实例方法 test()");
}
}

public class SubClass extends BaseClass {
// 子类实例变量 会隐藏父类实例变量
public String book = "子类1 实例变量";

// 重写父类实例方法
public void test() {
System.out.println("子类 重写的实例方法 test()");
}

public void sub() {
System.out.println("子类 自己定义的方法 sub()");
}

public static void main(String[] args) {
// bc的编译类型(bs前面的类型 BaseClass),运行时类型(new 后面的类型BaseClass)
BaseClass bc = new BaseClass();
// 输出6
System.out.println(bc.book);
// 调用的是父类的方法
bc.base();
bc.test();
System.out.println("-------------------------------");
// 编译时类型和运行时类型一样
SubClass sc = new SubClass();
// 调用子类 定义的book
System.out.println(sc.book);
// 调用子类 继承得到的 方法
sc.base();
// 调用子类 重写的 方法
sc.test();
System.out.println("-------------------------------");
// 编译时类型是BaseClass 运行时类型是SubClass
BaseClass ploymophicBc = new SubClass();
// 调用的是 被覆盖的 book
System.out.println(ploymophicBc.book);
// 调用的是 父类继承得到的base方法
ploymophicBc.base();
// 调用的是 子类重写的test方法
ploymophicBc.test();
// 无法调用 子类自己定义的sub方法,
// 因为引用ploymophicBc的编译时类型是父类BaseClass,
// 父类中没有定义sub方法所以无法调用
// ploymophicBc.sub();
System.out.println("---------------------------------");
ploymophicBc = new A();
System.out.println(ploymophicBc.book);
// 调用A实现的test方法
ploymophicBc.test();
}
}

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1024
父类 实例方法 base()
父类 实例方法 test()
-------------------------------
子类1 实例变量
父类 实例方法 base()
子类 重写的实例方法 test()
-------------------------------
1024
父类 实例方法 base()
子类 重写的实例方法 test()
---------------------------------
1024
子类A 实例方法 test()

从上面的运行结果中可以看出,

  • 当父类引用指向SubClass这个子类时,调用的test方法是子类SubClass重写的test方法,
  • 当父类引用指向A这个子类时,调用的test方法是子类A重写的test方法,

但是父类引用调用的实例变量一直都是父类中定义的实例变量,

默认访问编译时类型中定义的成员变量

通过引用变量来访问实例变量时,总是访问编译时类型中定义的成员变量