[JAVA · 初级]:10.如何更好的理解多态
2016-03-13 11:04
483 查看
说道多态,一定离不开其它两大特性:封装和继承。而多态是在它们的基础之上表现而来的,息息相关。在记忆中,每一次学习面向对象的时候,都与这三大特性有扯不开的关系,其是面向对象的重点,当然也算是难点。但是,它们就像是一层窗户纸,只要有一个缝隙,你就完全可以搞懂什么是面向对象。下面来看看关于多态的一些介绍吧:
公司主管角度:老板交代下来相同任务,每个人完成的情况不同;
专业角度:同种类的多个对象,在接收到同一个消息时却产生了不同反应和效果;
从代码形式上看: 父类的对象变量调用了子类中重写的方法(注意: 往往是有一个父类,而他有多个子类,且在这些子类中同时重写父类的某个方法);
结论:多态的前提是有一个父类,多个子类。
由于Object类是超根类(祖先类),因此,它的对象变量可以引用所有类的对象。
特殊运算符: instanceof 属性
注意: 子类的对象变量可以引用父类的对象吗?
答案: 不可以
若要进行,则必须强制转换。
即对象类型的转换分为以下两种:
1) 向上转型: 子类的对象可以直接赋给父类的对象变量。
这种现象也称之为类型的自动转换。
2) 向下转型: 将父类的引用强制转换成子类的引用。
注意: 它必须强制转换。
格式: (类型名) 对象变量;
当父类对象变量引用了子类的对象时,则问: 父类的对象变量是否可以直接调用子类的特有方法?
答案: 否定的。
例如: Animal 是父类, Dog是子类, 而getName()方法是Dog子类特有的方法。
因此, 当有 Anaimal a = new Dog(); 时,
则 a.getName(); 是否正确?
答: 错误。
问: 那如何访问?
答: 需要先强制转换(向下转型)
子类类名 对象变量 = (子类类名)父类的对象变量;
即: Dog d = (Dog)a;
之后, d.getName(); 此时,正确。
什么情况下需要将对象的引用实现强制转换(还原)(向下转型)?
1) 一定是发生多态:
父类的对象变量引用了子类的对象。
Object obj = new Student();
2)一定是想要去访问(调用)子类对象的特有属性或方法。
父类的对象变量.子类对象的特有方法(属性); //则错了。
Stringstr = obj.getSchool(); //错了。
((子类类名)父类的对象变量).子类对象的特有方法(属性); //则对了。
Stringstr = ((Student)obj).getSchool(); //对了。
//多态: 父类的对象变量引用了子类的对象。
哪个对象的。这种现象称之为动态绑定 (联编)。
结论: 动态绑定的前提是:
1) 发生继承,且一个父类有多个子类。
2) 在每一个子类中对继承自父类的同一个方法实现了重写。
3) 发生多态,即: 父类对象变量引用了不同的子类对象。
4) 父类的对象变量调用了重写的方法,即: 发生了动态绑定。从而实现了多的价值。
封装性: 安全性和重用性。
继承性: 高效性和重用性。
多态性: 统一性(有机性)和高效性。
对于这三大特性的理解不同,可能会有不同的体会,例如:大自然不同的风景呈现,我们享受到的也不同,如果每天都面对着同一幅场景,后果是很可怕的。要想更好的理解,不断地实践是很不错的选择呢!
多态,你一生都离不开的两个字,无论在哪里。
是什么?
文学角度:一千个读者就有一千零一个哈姆雷特;公司主管角度:老板交代下来相同任务,每个人完成的情况不同;
专业角度:同种类的多个对象,在接收到同一个消息时却产生了不同反应和效果;
从代码形式上看: 父类的对象变量调用了子类中重写的方法(注意: 往往是有一个父类,而他有多个子类,且在这些子类中同时重写父类的某个方法);
结论:多态的前提是有一个父类,多个子类。
做什么?
父类的对象变量可以引用本类的对象,也可以引用子类的对象。由于Object类是超根类(祖先类),因此,它的对象变量可以引用所有类的对象。
特殊运算符: instanceof 属性
注意: 子类的对象变量可以引用父类的对象吗?
答案: 不可以
若要进行,则必须强制转换。
即对象类型的转换分为以下两种:
1) 向上转型: 子类的对象可以直接赋给父类的对象变量。
这种现象也称之为类型的自动转换。
2) 向下转型: 将父类的引用强制转换成子类的引用。
注意: 它必须强制转换。
格式: (类型名) 对象变量;
当父类对象变量引用了子类的对象时,则问: 父类的对象变量是否可以直接调用子类的特有方法?
答案: 否定的。
例如: Animal 是父类, Dog是子类, 而getName()方法是Dog子类特有的方法。
因此, 当有 Anaimal a = new Dog(); 时,
则 a.getName(); 是否正确?
答: 错误。
问: 那如何访问?
答: 需要先强制转换(向下转型)
子类类名 对象变量 = (子类类名)父类的对象变量;
即: Dog d = (Dog)a;
之后, d.getName(); 此时,正确。
什么情况下需要将对象的引用实现强制转换(还原)(向下转型)?
1) 一定是发生多态:
父类的对象变量引用了子类的对象。
Object obj = new Student();
2)一定是想要去访问(调用)子类对象的特有属性或方法。
父类的对象变量.子类对象的特有方法(属性); //则错了。
Stringstr = obj.getSchool(); //错了。
((子类类名)父类的对象变量).子类对象的特有方法(属性); //则对了。
Stringstr = ((Student)obj).getSchool(); //对了。
//多态: 父类的对象变量引用了子类的对象。
Demo
<span style="font-size:18px;"><span style="font-size:18px;">public class AnimalsTest { public static void main(String[] args) { Animal a = new Animal("动物"); Dog d1 = new Dog("狗", "旺财", true, 2); Cat c1 = new Cat("猫", "豆豆", "blue"); System.out.println( a ); System.out.println( d1 ); System.out.println( c1 ); boolean flag = a instanceof Animal ; //判断a属于Animal类型吗 System.out.println( flag ); flag = a instanceof Dog; System.out.println( flag ); flag = d1 instanceof Dog; System.out.println( flag ); flag = d1 instanceof Animal; //当结果为true时,说明它们之间存在继承关系。 System.out.println( flag ); //多态: 父类的对象变量引用了子类的对象。 Animal a1 = new Dog("狗", "ABC", false, 3); //System.out.println( a1.getName() ); //调用a1的 getName()方法. 此时,它会去Animal类中找 getName()方法。若找不到,则报错。 //判断a1是否拥有Dog类的特性,若有,则还原。 if( a1 instanceof Dog ){ Dog dd = (Dog)a1; //将a1强制转换为Dog类型。 System.out.println( dd.getName() ); } System.out.println("程序结束。"); } } </span></span>
干什么?
在调用一个方法时,从源代码上看,无法确定调用了哪个对象的方法。只有在程序运行期间根据对象变量引用的实际对象才能确定此方法是哪个对象的。这种现象称之为动态绑定 (联编)。
结论: 动态绑定的前提是:
1) 发生继承,且一个父类有多个子类。
2) 在每一个子类中对继承自父类的同一个方法实现了重写。
3) 发生多态,即: 父类对象变量引用了不同的子类对象。
4) 父类的对象变量调用了重写的方法,即: 发生了动态绑定。从而实现了多的价值。
Demo
<span style="font-size:18px;"><span style="font-size:18px;">//由于此程序代码较多,只截取显示结果类: package com.hqg.oop.d37.a3; import com.hqg.oop.d37.a2.Circle; import com.hqg.oop.d37.a2.Rectangle; import com.hqg.oop.d37.a2.Shape; import com.hqg.oop.d37.a2.Triangle; //多态的应用 public class ShapesTestNew { public static void main(String[] args) { //声明并创建一个Shape类的数组,用来存放6个平面图形对象。 int x = (int)( 20 * Math.random()) + 1; Shape[] ss = new Shape[ x ]; //初始化: 创建6个平面图形对象并存放到数组的元素中。 for( int i = 0; i < ss.length; i++ ){ ss[i] = createShape(); // 将方法返回的结果赋给 ss[i] } //求所有图形的面积总和 double totalArea = 0; totalArea = sumArea( ss ); //将反馈的结果赋给 totalArea 变量 System.out.println("\n以上所有图形的面积总和为: " + totalArea ); } // 结束程序的运行。 //方法: 求所有图形的面积总和 public static double sumArea( Shape[] shapes ){ double sumArea = 0; for( int i = 0; i < shapes.length; i++ ){ sumArea += shapes[i].area(); //动态绑定。在程序运行期间求每个图形的面积并累加起来。 String str = shapes[i].toString(); //当前这个图形的全部信息 System.out.println( str ); System.out.println("--------------------------------------------------"); } return sumArea ; } //方法: 用来产生 Circle或Triangle或Rectangle三种类的对象之一。 public static Shape createShape(){ int n = (int)( 3 * Math.random()); //[0, 1, 2] switch( n ){ case 0 : double r = Math.round( 10 * Math.random() * 10 ) / 10.0 ; return new Circle("圆", r); case 1 : int l = (int)( 99 * Math.random()) + 1; int w = (int)( 69 * Math.random()) + 1; return new Rectangle("长方形", l , w ); case 2 : int a = (int)( 33 * Math.random() ) + 1; int h = (int)( 53 * Math.random() ) + 1; return new Triangle("三角形", a, h ); default : return null; } } }</span></span>
为什么?
关于面向对象的三大特性,为了更好地理解我们为什么要用它,很有必要知道它带来的好处。当然,站在巨人的肩膀上学习总是高效的,分享一下:封装性: 安全性和重用性。
继承性: 高效性和重用性。
多态性: 统一性(有机性)和高效性。
对于这三大特性的理解不同,可能会有不同的体会,例如:大自然不同的风景呈现,我们享受到的也不同,如果每天都面对着同一幅场景,后果是很可怕的。要想更好的理解,不断地实践是很不错的选择呢!
业务思想
学习的目的是为了更好地实践,要想在不断进行的项目中可以完成的更好、更为的轻松,这是离不开多态的。我们可以从最后一个demo中,看出加上动态绑定的图形选择,可以很有效的避免我们敲出很多图形的初始化代码,很高效,而且重复的初始化图形,很容易把我们惹毛的(当然这是开玩笑的),而我们要做的就是,尽量减少我们的工作负担,更为高效、轻松的完成。多态,你一生都离不开的两个字,无论在哪里。
相关文章推荐
- Leetcode:223. Rectangle Area(JAVA)
- spring 中属性scope 的prototype(有状态)和singleton(无状态)
- 【JAVA】38、构造方法的定义
- 超轻量级DI容器框架Google Guice与Spring框架的区别教程详解及其demo代码片段分享
- 排序算法总结 java实现
- 常见排序算法的Java实现
- java排序
- 使用java中replaceAll方法替换字符串中的反斜杠
- Java系统调优:内存管理与垃圾回收
- JavaSE知识集锦(1)深拷贝与浅拷贝
- java设计模式——结构型之门面模式
- java单点登录系统CAS的简单使用
- JDK动态代理和CGLiB动态代理
- JAVA HASHMAP 如何用
- java中HashMap详解
- [Java] i++与++i的区别(后缀++与前缀++)
- Java运算符优先级
- Ubuntu 15.10 程序“java”已包含在下列软件包中,安装jdk1.8.73
- Java编程规范
- 线程