最近面试被问到Java的多态怎么理解,自己含含糊糊的感觉自己知道点含义可是就是具体怎么说打不出来,尴尬死。接下来打算把三大特性:封装、继承、多态都好好的写下来,以参考。本文先写关于Java的多态吧。
多态,用一句话来说,就是事物在运行过程中存在不同的状态。多态的存在有三个前提:
- 要有继承关系
- 子类要重写父类的方法
- 父类引用指向子类对
实验
我们先定义一个父类Animal
1 | class Animal{ |
再定义一个子类Cat
1 | class Cat extends Animal{ |
再来一个测试
1 | class Demo_Test{ |
先来看看上面是否满足三个条件:
- 子类Cat继承了父类Animal;
- 子类重写了父类的eat()、sleep()方法,其中前者是非静态,后者是静态方法;
- 在堆内存中开辟了子类Cat()对象,并把存在于栈内存中的Animal()引用指向这个对象。
然后我们看一下运行结果,应该是
猫在吃饭
动物在睡觉
动物在奔跑
10
20
那么为什么呢?
- 子类重写了父类的非静态成员方法,对象am.eat()的输出结果是“猫在吃饭”;
- 子类重写了父类的静态成员方法,对象am.sleep()的输出结果是“动物在睡觉”;
- 未被子类重写的方法am.run()的输出结果是“动物在奔跑”。
那么可以总结出多态成员访问的特点:
- 成员变量:编译看左边(父类),运行看左边(父类)
- 成员方法:编译看左边(父类),运行看右边(子类)。动态绑定
- 静态方法:编译看左边(父类),运行看左边(父类)(静态和类相关,算不上重写,所以,访问还是左边的)
只有非静态的成员方法,编译看左边,运行看右边,而以上的这个过程就叫做多态的向上转型。
多态的缺点
多态也存在缺点,也就是无法尝试调用子类特有的方法,如上面未展示am.catchMouse();
System.out.println(am.name);两行代码都是会报错的,因为这是子类特有的成员方法和成员属性。
如果我们需要调用子类的特有成员方法和成员属性,那么应该将这个父类强制转换为子类类型,如
1 | class Demo_Test { |
执行强制类型转换Cat ct = (Cat)am
后,这时ct就指向堆里最先创建的Cat()对象了,自然能使用Cat类的一切成员方法和成员属性了。这也是多态的魅力,为了使用子类的某些方法不需要重新再开辟内存。以上就是多态中的向下转型。
通俗解释
花木兰替父从军
大家都知道花木兰替父从军的例子,花木兰替父亲花弧从军。那么这时候花木兰是子类,花弧是父类。花弧有自己的成员属性年龄,姓名,性别。花木兰也有这些属性,但是很明显二者的属性完全不一样。花弧有自己的非静态成员方法‘骑马杀敌’,同样花木兰也遗传了父亲一样的方法‘骑马杀敌’。花弧还有一个静态方法‘自我介绍’,每个人都可以问花弧姓甚名谁。同时花木兰还有一个自己特有的非静态成员方法‘涂脂抹粉’。但是,现在花木兰替父从军,女扮男装。这时候相当于父类的引用(花弧这个名字)指向了子类对象(花木兰这个人),那么在其他类(其他的人)中访问子类对象(花木兰这个人)的成员属性(姓名,年龄,性别)时,其实看到的都是花木兰她父亲的名字(花弧)、年龄(60岁)、性别(男)。当访问子类对象(花木兰这个人)的非静态成员方法(骑马打仗)时,其实都是看到花木兰自己运用十八般武艺在骑马打仗。当访问花木兰的静态方法时(自我介绍),花木兰自己都是用她父亲的名字信息在向别人作自我介绍。并且这时候花木兰不能使用自己特有的成员方法‘涂脂抹粉’。—–多态中的向上转型
那么终于一将功成万骨枯,打仗旗开得胜了,花木兰告别了战争生活。有一天,遇到了自己心爱的男人,这时候爱情的力量将父类对象的引用(花弧这个名字)强制转换为子类对象本来的引用(花木兰这个名字),那么花木兰又从新成为了她自己,这时候她完全是她自己了。名字是花木兰,年龄是28,性别是女,打仗依然那样生猛女汉子,自我介绍则堂堂正正地告诉别人我叫花木兰。OMG!终于,终于可以使用自己特有的成员方法‘涂脂抹粉’了。从此,花木兰完全回到了替父从军前的那个花木兰了。并且和自己心爱的男人幸福的过完了一生。—–多态中的向下转型
。
参考出处
- 作者:程序狗
链接:https://www.zhihu.com/question/30082151/answer/120520568
来源:知乎