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 类型,类型不匹配,直接报错

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

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

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

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