父类显式向子类强转的思考

父类显式向子类强转的思考

1. 起因

看到一道面试题:

public class Animal {
    private String name = "animal_name";

    public void getName() {
        System.out.println(name);
    }

    public void setName() {
        name = "aaa";
    }

    public void eat() {
        System.out.println("animal_eat");
    }

}
public class Dog extends Animal{

    @Override
    public void eat() {
        System.out.println("dog_eat");
    }

    public static void main(String[] args) {
        // 流程1
        Animal animal_1 = new Dog();
        Dog dog = (Dog) animal_1;
        dog.setName();
        animal_1.getName();

		// 流程2
        Animal animal_2 = new Animal();
        Dog d = (Dog) animal_2;
        d.eat();
    }

}

答案是:输出aaa后报错。

2. 个人思考

  • 流程1(引用类型实例对象类型

​ 1)父子间的继承关系很好懂,子类重写eat方法,继承name属性和get set方法。

​ 2)内存空间,在堆中创建Dog的实例对象,栈中保存Animal类型的引用,指向堆中的Dog实例。

​ 3)编译期时,会检查引用类型实例对象类型是否正确。举例,

Dog dog = (Dog) animal_1;

​ 编译器相信dog引用 确实 指向了真实的Dog实例,但是在编译期无法证实,于是检查animal_1 的引用类型和dog引用类型 的父子关系。Dog类型和Animal类型的继承关系会通过编译,而下面示例不会通过,因为String类型和Animal类型不存在继承,

String dog_str = (String) animal_1;

​ 4)运行时期,检查引用类型和实例类型是否正确。

animal_1d 实例类型确实是Dog,只是它的引用类型是Animal(多态)。于是dog引用指向了animal_1d 实例。

  • 流程2

Animal animal_2 = new Animal();

Dog d = (Dog) animal_2;

​ 1)animal_2 实例对象类型和引用类型都是Animal。

​ 2)编译器在编译期相信 d 的引用指向了真实的Dog类型(但实际上不是),随后检查d的引用类型与 animal_2 的引用类型, 能够进行强转,编译期通过。

​ 3)运行期检查animal_2 的实例对象类型是Animal,抛出ClassCastException

3. instansof

显式类型向下强转前,使用instanceof进行实例判断:

public class Dog extends Animal{

	//...略

    public static void main(String[] args) {
        Animal animal_1 = new Dog();
        if (animal_1 instanceof Dog) {
            Dog dog = (Dog) animal_1;
            dog.setName();
            animal_1.getName();
        }

        Animal animal_2 = new Animal();
        if (animal_2 instanceof Dog) {
            Dog d = (Dog) animal_2;
            d.eat();
        }

    }

}

参考链接:

stackoverflow

posted @ 2023-02-25 01:46  OraCat  阅读(12)  评论(0编辑  收藏  举报