Polymorphism of Java

最近面试被问到Java的多态怎么理解,自己含含糊糊的感觉自己知道点含义可是就是具体怎么说打不出来,尴尬死。接下来打算把三大特性:封装、继承、多态都好好的写下来,以参考。本文先写关于Java的多态吧。

多态,用一句话来说,就是事物在运行过程中存在不同的状态。多态的存在有三个前提:

  1. 要有继承关系
  2. 子类要重写父类的方法
  3. 父类引用指向子类对

实验

我们先定义一个父类Animal

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Animal{
int num = 10;
int age = 20;

public void eat(){
System.out.println("动物在吃饭");
}

public stastic void sleep(){
System.out.println("动物在睡觉");
}

public void run(){
System.out.println("动物在奔跑");
}
}

再定义一个子类Cat

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Cat extends Animal{
int num = 30;
int age = 40;
String name = "Tom";

public void eat(){
System.out.println("猫在吃饭");
}

public stastic void sleep(){
System.out.println("猫在睡觉");
}

public void catchMouse(){
System.out.println("猫在抓老鼠");
}
}

再来一个测试

1
2
3
4
5
6
7
8
9
10
11
12
13
class Demo_Test{
public stastic void main(String[] args){
Animal am = new Cat();
am.eat();
am.sleep();
am.run();
System.out.println(am.num);
System.out.println(am.age);

am.catchMouse();
System.out.println(am.name);
}
}

先来看看上面是否满足三个条件:

  1. 子类Cat继承了父类Animal;
  2. 子类重写了父类的eat()、sleep()方法,其中前者是非静态,后者是静态方法;
  3. 在堆内存中开辟了子类Cat()对象,并把存在于栈内存中的Animal()引用指向这个对象。

然后我们看一下运行结果,应该是

猫在吃饭
动物在睡觉
动物在奔跑
10
20

那么为什么呢?

  • 子类重写了父类的非静态成员方法,对象am.eat()的输出结果是“猫在吃饭”;
  • 子类重写了父类的静态成员方法,对象am.sleep()的输出结果是“动物在睡觉”;
  • 未被子类重写的方法am.run()的输出结果是“动物在奔跑”。

那么可以总结出多态成员访问的特点:

  • 成员变量:编译看左边(父类),运行看左边(父类)
  • 成员方法:编译看左边(父类),运行看右边(子类)。动态绑定
  • 静态方法:编译看左边(父类),运行看左边(父类)(静态和类相关,算不上重写,所以,访问还是左边的)

只有非静态的成员方法,编译看左边,运行看右边,而以上的这个过程就叫做多态的向上转型

多态的缺点

多态也存在缺点,也就是无法尝试调用子类特有的方法,如上面未展示am.catchMouse();
System.out.println(am.name);两行代码都是会报错的,因为这是子类特有的成员方法和成员属性。

如果我们需要调用子类的特有成员方法和成员属性,那么应该将这个父类强制转换为子类类型,如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Demo_Test {
public static void main(String[] args) {

Animal am = new Cat();
am.eat();
am.sleep();
am.run();
// am.catchMouse();
// System.out.println(am.name);
System.out.println(am.num);
System.out.println(am.age);

System.out.println("------------------------------");
Cat ct = (Cat)am;
ct.eat();
ct.sleep();
ct.run();
ct.catchMouse();
}
}

执行强制类型转换Cat ct = (Cat)am后,这时ct就指向堆里最先创建的Cat()对象了,自然能使用Cat类的一切成员方法和成员属性了。这也是多态的魅力,为了使用子类的某些方法不需要重新再开辟内存。以上就是多态中的向下转型

通俗解释

花木兰替父从军
大家都知道花木兰替父从军的例子,花木兰替父亲花弧从军。那么这时候花木兰是子类,花弧是父类。花弧有自己的成员属性年龄,姓名,性别。花木兰也有这些属性,但是很明显二者的属性完全不一样。花弧有自己的非静态成员方法‘骑马杀敌’,同样花木兰也遗传了父亲一样的方法‘骑马杀敌’。花弧还有一个静态方法‘自我介绍’,每个人都可以问花弧姓甚名谁。同时花木兰还有一个自己特有的非静态成员方法‘涂脂抹粉’。但是,现在花木兰替父从军,女扮男装。这时候相当于父类的引用(花弧这个名字)指向了子类对象(花木兰这个人),那么在其他类(其他的人)中访问子类对象(花木兰这个人)的成员属性(姓名,年龄,性别)时,其实看到的都是花木兰她父亲的名字(花弧)、年龄(60岁)、性别(男)。当访问子类对象(花木兰这个人)的非静态成员方法(骑马打仗)时,其实都是看到花木兰自己运用十八般武艺在骑马打仗。当访问花木兰的静态方法时(自我介绍),花木兰自己都是用她父亲的名字信息在向别人作自我介绍。并且这时候花木兰不能使用自己特有的成员方法‘涂脂抹粉’。—–多态中的向上转型
那么终于一将功成万骨枯,打仗旗开得胜了,花木兰告别了战争生活。有一天,遇到了自己心爱的男人,这时候爱情的力量将父类对象的引用(花弧这个名字)强制转换为子类对象本来的引用(花木兰这个名字),那么花木兰又从新成为了她自己,这时候她完全是她自己了。名字是花木兰,年龄是28,性别是女,打仗依然那样生猛女汉子,自我介绍则堂堂正正地告诉别人我叫花木兰。OMG!终于,终于可以使用自己特有的成员方法‘涂脂抹粉’了。从此,花木兰完全回到了替父从军前的那个花木兰了。并且和自己心爱的男人幸福的过完了一生。—–多态中的向下转型

参考出处