杰瑞科技汇

Java子类对象赋值给父类对象,底层机制是什么?

什么是向上转型?

向上转型是指将一个子类的实例赋值给其父类引用变量的过程。

Java子类对象赋值给父类对象,底层机制是什么?-图1
(图片来源网络,侵删)

这就像说:“一个 Dog(狗)是一个 Animal(动物)”,所以我们可以用一个 Animal 类型的变量来指向一个 Dog 对象。

语法格式:

父类 父类引用 = new 子类构造方法();

示例代码:

class Animal {
    public void eat() {
        System.out.println("动物吃东西");
    }
}
class Dog extends Animal {
    // Dog 继承了 Animal 的所有成员
    @Override
    public void eat() {
        System.out.println("狗吃骨头");
    }
    // Dog 特有的方法
    public void bark() {
        System.out.println("汪汪叫");
    }
}
public class Main {
    public static void main(String[] args) {
        // 1. 创建一个子类对象
        Dog myDog = new Dog();
        // 2. 向上转型:将子类对象赋值给父类引用
        Animal myAnimal = myDog; // 或者写成 Animal myAnimal = new Dog();
        System.out.println("向上转型完成。");
    }
}

向上转型的特点和规则

向上转型后,myAnimal 这个父类引用变量会表现出一些有趣的行为,我们可以总结为以下几点:

Java子类对象赋值给父类对象,底层机制是什么?-图2
(图片来源网络,侵删)

访问成员变量:编译看左边(父类),运行看左边(父类)

  • 访问成员变量:当通过父类引用访问成员变量时,访问的是父类中定义的变量,忽略子类中重名的变量
  • 访问成员方法:当通过父类引用访问成员方法时,如果该方法被子类重写了,那么实际调用的是子类中重写后的方法(动态绑定/运行时多态),如果没有被重写,则调用父类中的方法。

示例代码:

class Animal {
    int age = 5; // 父类的成员变量
    public void eat() {
        System.out.println("动物吃东西");
    }
}
class Dog extends Animal {
    // 子类重写了父类的成员变量
    int age = 3;
    // 子类重写了父类的方法
    @Override
    public void eat() {
        System.out.println("狗吃骨头");
    }
    public void bark() {
        System.out.println("汪汪叫");
    }
}
public class Main {
    public static void main(String[] args) {
        Dog myDog = new Dog();
        Animal myAnimal = myDog; // 向上转型
        // 访问成员变量:编译看左边(Animal),运行看左边(Animal)
        System.out.println(myAnimal.age); // 输出 5,访问的是父类 Animal 的 age
        // 访问成员方法:编译看左边(Animal),运行看右边(Dog)
        myAnimal.eat(); // 输出 "狗吃骨头",调用的是子类 Dog 重写后的 eat() 方法
    }
}

不能访问子类特有的成员

父类引用变量只知道父类“有什么”,不知道子类“新增了什么”,它不能直接访问子类中新增的成员变量或方法

示例代码(接上例):

public class Main {
    public static void main(String[] args) {
        Dog myDog = new Dog();
        Animal myAnimal = myDog; // 向上转型
        // 下面的代码会编译报错!
        // myAnimal.bark(); // Error: cannot find symbol method bark()
    }
}

编译器会报错,因为 Animal 类中没有定义 bark() 方法。

Java子类对象赋值给父类对象,底层机制是什么?-图3
(图片来源网络,侵删)

为什么要使用向上转型?(核心优势)

向上转型是实现 多态 的基石,它主要有两大好处:

提高代码的扩展性和灵活性

我们可以定义一个方法,其参数类型为父类,这样该方法就可以接收任何子类的对象,而无需为每个子类都写一个重载方法。

示例:

class Animal {
    public void eat() {
        System.out.println("动物吃东西");
    }
}
class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("狗吃骨头");
    }
}
class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
}
// 定义一个通用的方法,可以处理所有 Animal 的子类
public class ZooKeeper {
    public void feed(Animal animal) { // 参数是父类类型
        animal.eat();
    }
    public static void main(String[] args) {
        ZooKeeper keeper = new ZooKeeper();
        // 传入 Dog 对象
        keeper.feed(new Dog()); // 输出 "狗吃骨头"
        // 传入 Cat 对象
        keeper.feed(new Cat()); // 输出 "猫吃鱼"
        // 传入 Animal 对象
        keeper.feed(new Animal()); // 输出 "动物吃东西"
    }
}

如果没有向上转型,feed 方法就需要写成 feed(Dog dog), feed(Cat cat) 等多个重载版本,非常繁琐且难以扩展。

方便实现多态

多态的字面意思是“多种形态”,在 Java 中,它意味着“一个接口,多种实现”,向上转型使得我们可以用统一的方式处理不同的子类对象,而在运行时根据对象的实际类型来执行其对应的方法。


如何恢复子类的全部功能?(向下转型)

向上转型后,如果确实需要调用子类特有的方法,就需要将父类引用“转回”子类引用,这个过程称为 向下转型

重要:向下转型之前,必须先用 instanceof 关键字进行类型检查,否则可能会抛出 ClassCastException(类型转换异常)。

语法格式:

if (父类引用 instanceof 子类) {
    子类 子类引用 = (子类) 父类引用;
    // 现在可以安全地调用子类特有的方法了
}

示例代码:

class Animal {
    public void eat() {
        System.out.println("动物吃东西");
    }
}
class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("狗吃骨头");
    }
    public void bark() {
        System.out.println("汪汪叫");
    }
}
public class Main {
    public static void main(String[] args) {
        // 1. 向上转型
        Animal myAnimal = new Dog();
        // 2. 向下转型
        if (myAnimal instanceof Dog) { // 必须先检查类型
            Dog myDog = (Dog) myAnimal; // 安全地向下转型
            myDog.bark(); // 现在可以调用子类特有的方法了,输出 "汪汪叫"
        }
        // 错误的向下转型示例
        Animal myAnimal2 = new Animal();
        // 下面的代码会抛出 ClassCastException,因为 Animal 对象不是 Cat
        // Cat myCat = (Cat) myAnimal2; 
    }
}

特性 描述 示例
向上转型 子类对象 -> 父类引用 Animal a = new Dog();
访问成员变量 编译看左边,运行看左边,访问父类变量。 System.out.println(a.age); // 访问 Animalage
访问成员方法 编译看左边,运行看右边,调用子类重写的方法。 a.eat(); // 调用 Dogeat()
访问子类特有成员 不能直接访问,编译器会报错。 a.bark(); // 编译错误!
向下转型 父类引用 -> 子类引用,必须用 instanceof 检查。 if (a instanceof Dog) { Dog d = (Dog) a; d.bark(); }
核心目的 实现多态,提高代码的扩展性和灵活性。 可以用 Animal 类型的参数接收所有子类对象。

向上转型是一种“向上看齐”的行为,它让你能站在父类的视角看待所有子类,从而获得代码的通用性和扩展性,而当你需要具体操作某个子类的“独门绝技”时,再通过向下转型“降维打击”,调用其特有的方法。

分享:
扫描分享到社交APP
上一篇
下一篇