Java入门-多态

Java 引用变量有两个类型。如果编译时类型和运行时类型不一致,就可能出现多态。

  • 编译时类型:由声明该变量时使用的类型决定

  • 运行时类型:由实际运行时赋给该变量的对象决定

向上类型转换

示例代码:

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
public class BaseClass {
public int book = 6;

public void base() {
System.out.println("父类的普通方法");
}

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

public class SubClass extends BaseClass {
public String book = "轻量级 Java EE";

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

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

public static void main(String[] args) {
BaseClass ploymophicBc = new SubClass();
System.out.println(ploymophicBc.book);
ploymophicBc.base();
ploymophicBc.test();
// 因为 ploymophicBc 的编译时类型是 BaseClass
// BaseClass 类没有提供 sub 方法,所以下面代码编译时会出错
// ploymophicBc.sub();
}
}

上面的例子中,引用变量 ploymophicBc 比较特殊,它的编译时类型是 BaseClass,而运行时类型是 SubClass。

ploymophicBc.sub() 这行代码会在编译时报错,因为 ploymophicBc 编译时类型为 BaseClass,而 BaseClass 中没有定义 sub 方法,因此编译时无法通过。

但是注意,ploymophicBc.book 的值为 6, 而不是 ”轻量级 Java EE“。因为对象的实例变量不具备多态性,系统总是试图访问它编译时类型所定义的成员变量,而非运行时。

小结

子类其实是一种特殊的父类,因此 java 允许把父类的引用指向子类对象,这被称为向上转型(upcasting),向上转型由系统自动完成。

可以调用哪些方法,取决于引用类型(编译时)。

具体调用哪个方法,取决于引用指向的实例对象(运行时)。

向下类型转换

问题:引用变量在代码编译过程中,只能调用它编译时类型具备的方法,而不能调用它运行时类型具备的方法

解决:强制转换成运行时类型

方法:引用类型之间的转换只能在有继承关系的两个类型之间进行,否则编译出错。如果想把一个父类引用变量的编译时类型转换成子类类型,则这个引用变量的运行时类型得是子类类型,否则引发 ClassCastException 异常

示例代码:

1
2
3
4
5
6
7
8
9
10
//创建子类对象
Dog dog = new Dog();

// 向上类型转换(类型自动提升),不存在风险
// 此时 animal 为 Dog 类型
Animal animal = dog;

// 风险演示:编译阶段不会报错,但是运行时会报错
// 根据上面的语句,此时 animal 指向 Dog 类型对象,没有办法转化成 Cat 对象。
Cat cat = (Cat)animal; // 1.强转成功,编译时按 Cat 类型 2. 但运行时 Dog 类型,类型不匹配,直接报错

instanceof

为了解决强制类型转换,可能引发的 ClassCastException 异常,引入 instanceof 运算符。

instanceof 运算符的含义:用于判断左边的对象(运行时类型)是否是右边的类或者其子类、实现类的实例。如果是返回 true,否则返回 false。

在之前的代码中,强制类型转换前使用 instanceof 判断:

1
2
3
if (anmial instanceof Cat) {
Cat cat = (Cat)animal;
}