【JAVA】`Animal a = new Dog();`到底是什么意思?

认真看这句代码 Animal a = new Dog();

  • 一个对象 a
  • a 的引用是 Animal
  • 用来创建这个 a 对象的是 Dog()

在java 中引用 ≠ 对象 Animal 是一个引用他就像一个遥控器一样,指向 a 这个对象,但是这个 a 对象实际的东西还是 Dog.只是 Animal 决定了 a 对象能用什么方法

在这一句话中有两个关键点,我们逐一比对 1. a 对象实际的东西还是 Dog 2. Animal 决定了 a 对象能用什么方法

1. a 对象实际的东西还是 Dog

假设我们有一段这样的代码

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
class Animal { // 父类 Animal
public void eat() {
System.out.println("Animal eat something");
}
}

class Dog extends Animal { // 子类 Dog
public void eat() {
System.out.println("Dog eat something");
}
}

class Cat extends Animal { // 子类 Cat
public void eat() {
System.out.println("Cat eat something");
}
}

public class Main {
public static void main(String[] args) {
Animal a = new Dog(); // <- 注意这里
Animal b = new Cat();
a.eat(); // 输出 Dog eat something
b.eat(); // 输出 Cat eat something
}
}
虽然 Animal 有自己的 eat 方法,但是具体 eat 方法怎么实现,还是要看子类的脸色

类比我们在看电视 - 遥控器把台选好了 - 但是电视放什么,还是电视台自己说了算

2. Animal 决定了 a 对象能用什么方法

假设有一段代码是这样的

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
class Animal { // 父类 Animal
public void eat() {
System.out.println("Animal eat something");
}
public void drink() {
System.out.println("Animal drink something");
}
}

class Dog extends Animal { // 子类 Dog
public void eat() {
System.out.println("Dog eat something");
}
public void walk() {
System.out.println("Dog walk");
}
}

public class Main {
public static void main(String[] args) {
Animal a = new Dog(); // <- 注意这里
a.eat(); // 输出 Dog eat something
a.drink(); // 输出 Animal drink something
a.walk(); // 错误!
}
}
我们发现我们并不能执行a.walk();这一段代码,因为 Animal 这个遥控器根本就没有 walk,他只有 eat 和 drink,所以我们用不了 walk,只能用 eat 和 drink

类比我们在看电视 - 实际上电视里面有 100 个台 - 但是遥控器只有 10 个台 - 所以导致我们只能看 10 个台(别的台我们也看不了了,因为遥控器摇不到)


那为什么不直接写 Dog a = new Dog();

好问题!如果只看这一行,确实没区别。但用父类引用有两个巨大好处:

1. 写通用代码

1
2
3
4
5
6
7
public static void feed(Animal a) {
a.eat();
}

feed(new Dog()); // 可以
feed(new Cat()); // 可以
feed(new Pig()); // 也可以(只要 Pig 继承 Animal)

如果feed 的参数类型写死成 Dog,那就只能喂狗了。用 Animal任何动物子类都能传进来

2. 用一个集合装不同的子类对象

1
2
3
4
5
6
7
8
Animal[] zoo = new Animal[3];
zoo[0] = new Dog();
zoo[1] = new Cat();
zoo[2] = new Pig();

for (Animal a : zoo) {
a.eat(); // 每个动物都用自己的方式吃
}

如果不用父类引用,你就没法把狗、猫、猪放进同一个数组里


一句话总结

Animal a = new Dog() 的意思是: 实际创建的是 Dog 对象(决定了实际行为),但用 Animal 类型的引用来操作它(决定了能调用哪些方法)。 这种用父类的眼光看子类对象的写法,是 Java 实现多态和写出灵活、可扩展代码的基础。