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

这就像说:“一个 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 这个父类引用变量会表现出一些有趣的行为,我们可以总结为以下几点:

访问成员变量:编译看左边(父类),运行看左边(父类)
- 访问成员变量:当通过父类引用访问成员变量时,访问的是父类中定义的变量,忽略子类中重名的变量。
- 访问成员方法:当通过父类引用访问成员方法时,如果该方法被子类重写了,那么实际调用的是子类中重写后的方法(动态绑定/运行时多态),如果没有被重写,则调用父类中的方法。
示例代码:
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() 方法。

为什么要使用向上转型?(核心优势)
向上转型是实现 多态 的基石,它主要有两大好处:
提高代码的扩展性和灵活性
我们可以定义一个方法,其参数类型为父类,这样该方法就可以接收任何子类的对象,而无需为每个子类都写一个重载方法。
示例:
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); // 访问 Animal 的 age |
| 访问成员方法 | 编译看左边,运行看右边,调用子类重写的方法。 | a.eat(); // 调用 Dog 的 eat() |
| 访问子类特有成员 | 不能直接访问,编译器会报错。 | a.bark(); // 编译错误! |
| 向下转型 | 父类引用 -> 子类引用,必须用 instanceof 检查。 |
if (a instanceof Dog) { Dog d = (Dog) a; d.bark(); } |
| 核心目的 | 实现多态,提高代码的扩展性和灵活性。 | 可以用 Animal 类型的参数接收所有子类对象。 |
向上转型是一种“向上看齐”的行为,它让你能站在父类的视角看待所有子类,从而获得代码的通用性和扩展性,而当你需要具体操作某个子类的“独门绝技”时,再通过向下转型“降维打击”,调用其特有的方法。
