## Java编程那些事儿63—多态性
郑州游戏学院陈跃峰
出自:[http://blog.csdn.net/mailbomb](http://blog.csdn.net/mailbomb)
#### 8.5.3 多态性
多态性是面向对象技术中最灵活的特性,主要是增强项目的可扩展性,提高代码的可维护性。
多态性依赖继承特性,可以把多态理解为继承性的扩展或者深入。
在这里把多态性分为两方面来进行介绍,对象类型的多态和对象方法的多态。
为了方便后续的讲解,首先给出一个继承结构的示例。
~~~
//文件名:SuperClass.java
public class SuperClass{
public void test(){
System.out.println(“SuperClass”);
}
}
// 文件名:SubbClass1.java
public class SubbClass1 extends SuperClass{
public void test(){
System.out.println(“SubbClass1”);
}
}
// 文件名:SubbClass2.java
public class SubbClass2 extends SuperClass{
public void test(){
System.out.println(“SubbClass2”);
}
}
~~~
在该示例代码中,SubbClass1和SubbClass2是SuperClass的子类,并且在子类的内部都覆盖父类中的test方法。由于这三个类中都书写构造方法,则按照默认构造方法的约定,每个类中都会被自动添加一个默认的构造方法。
##### 8.5.3.1 对象类型的多态
对象类型的多态是指声明对象的类型不是对象的真正类型,而对象的真正类型由创建对象时调用的构造方法进行决定。例外,按照继承性的说明,子类的对象也是父类类型的对象,可以进行直接赋值。
例如如下代码:
SuperClass sc = new SubbClass1();
这里声明了一个SuperClass类型的对象sc,然后使用SuperClass的子类SubbClass1的构造方法进行创建,因为子类类型的对象也是父类类型的对象,所以创建出来的对象可以直接赋值给父类类型的对象sc。除了对象的赋值以外,另外一个更重要的知识是sc对象虽然使用SuperClass声明的类型,但是内部存储的却是SubbClass1类型的对象。这个可以Java语言的中instanceof运算符进行判断。
instanceof是一个运算符,其作用是判断一个对象是否是某个类类型的对象,如果成立则表达式的值为true,否则为false。语法格式如下:
对象名 instanceof 类名
需要注意的是:这里的类名必须和声明对象时的类之间存储继承关系,否则将出现语法错误。
测试类型的代码如下:
~~~
/**
* 测试对象类型的多态
*/
public class TestObjectType {
public static void main(String[] args) {
SuperClass sc = new SubbClass1();
boolean b = sc instanceof SuperClass;
boolean b1 = sc instanceof SubbClass1;
System.out.println(b);
System.out.println(b1);
}
}
~~~
该测试程序的输出结果是:
true
true
由程序运行结果可以看出,sc既是SuperClass类型的对象,也是SubbClass1类型的对象,而SubbClass1的类型被隐藏起来了,这就是对象的多态。其实sc对象不仅仅在类型上是SubbClass1类型的,其存储的内容也是SubbClass1的内容,具体参看后面介绍的对象方法的多态。
对象类型的多态有很多的用途,极大的方便了对象的存储和传递,使代码很方便的进行扩展,对于已有代码不产生影响。下面介绍两个基本的使用。
1.对象的存储
在存储一系列不同子类的对象时,可以使用父类的结构来进行声明,这样可以方便数据的存储,例如需要存储多个SubbClass1和SubbClass2的对象时,则可以声明一个SuperClass类型的数组进行存储,示例代码如下:
SuperClass sc[] = new SuperClass[3];
sc[0] = new SubbClass1();
sc[1] = new SubbClass2();
sc[2] = new SubbClass1();
则这里的数组sc,可以存储各个类型子类的对象,而数组中每个元素的值都是存储的对应子类的对象,而只是在名义上的类型(语法上的类型)是SuperClass类型的,这样将方便程序的控制,当增加新的子类类型时,已有的代码不需要进行改造就可以自动适应新的子类的结构。
例如新增了一个SuperClass的子类SubbClass3,则该数组的代码可以修改成如下:
SuperClass sc[] = new SuperClass[3];
sc[0] = new SubbClass1();
sc[1] = new SubbClass2();
sc[2] = new SubbClass3();
其它的代码都需要进行修改,就可以适应新的结构,这是多态性最主要的用途。
2.对象的传递
在方法的传入参数传递,以及返回值处理方面都从对象类型的多态中受益。在向方法中传入参数时,如果该方法需要处理各个子类的对象,则只需要书写一个接受父类类型对象的方法即可。例如:
public void testObjectTypeMethod(SuperClass sc){}
则该在调用该方法时,可以传入SuperClass的对象,也可以传入其子类的对象,如果传入的是子类的对象,则子类对象中的内容不会丢失。例如调用的示例代码如下:
SuperClass sc = new SuperClass();
SubbClass1 sc1 = new SubbClass1();
SubbClass2 sc2 = new SubbClass2();
testObjectTypeMethod(sc);
testObjectTypeMethod(sc1);
testObjectTypeMethod(sc2);
这里说明的只是调用时的语法结构,这样的特性将使我们只需要书写一个方法,就可以处理所有子类的对象,简化代码的书写,降低代码的重复,从而降低维护的难度。
另外,方法的返回值也可以利用到该特性,例如如下方法:
public SuperClass testObjectTypeMethod2(){}
则在该方法的内部,既可以返回SuperClass类型的对象,也可以返回其子类的对象,也能简化代码的书写,便于代码的阅读和维护。
关于对象类型的多态,就简单的说明这么多,具体在项目中如何进行使用,还需要一定的技巧和方法。
##### 8.5.3.2 对象方法的多态
对象方法的多态基于方法的覆盖,也就是该对象调用的方法具体是子类的方法还是父类的方法,由创建对象时使用的构造方法决定,而不是由声明对象时声明的类型决定。
示例代码如下:
~~~
/**
* 测试对象方法的多态
*/
public class TestObjectMethod {
public static void main(String[] args) {
SuperClass sc = new SuperClass();
SubbClass1 sc1 = new SubbClass1();
SubbClass2 sc2 = new SubbClass2();
SuperClass sc3 = new SubbClass1();
testObjectTypeMethod(sc);
testObjectTypeMethod(sc1);
testObjectTypeMethod(sc2);
testObjectTypeMethod(sc3);
}
public static void testObjectTypeMethod(SuperClass sc){
sc.test(); //调用被覆盖的方法
}
}
该代码的执行结果如下:
SuperClass
SubbClass1
SubbClass2
SubbClass1
~~~
则从代码的执行结果看,虽然testObjectTypeMethod方法接收的是SuperClass类型的对象,但是传入子类对象时,子类对象的内容没有丢失,所以在调用test方法时,还是调用的对应对象中对应的test方法。
这样就在功能上实现了对象的传递,从而保留了对象的内容,极大的方便了代码的扩展性。
但是,由于Java在执行程序时,在程序运行的过程中,需要判断对象调用的具体是父类的方法还是子类的方法,所以程序的执行速度会稍微有所降低。
- 前言
- (1)序言
- (2)程序设计是什么?
- (3)你适合学习程序设计吗?
- (4)如何学好程序设计?
- (5)程序设计介绍小结
- (6)计算机软件基本概念
- (7)进制的概念
- (8)计算机内部的数据表达
- (9)网络编程基础
- (10)Java语言简介
- (11)JDK的获得、安装和配置
- (12)第一个HelloWorld程序
- (13)Eclipse基本使用
- (14)Eclipse基础使用进阶
- (15)如何学好Java语法
- (16)代码框架、关键字和标识符
- (17)基本数据类型
- (18)变量和常量
- (19)数据类型转换
- (20)空白、语句结束和注释
- (21)算术运算符
- (22)比较运算符
- (23)逻辑运算符
- (24)赋值运算符
- (25)位运算符
- (26)移位运算符
- (27)其它运算符
- (28)运算符优先级
- (29)表达式
- (30)流程控制基础
- (31)if语句语法(1)
- (32)if语句语法(2)
- (33)if语句语法(3)
- (34)switch语句语法
- (35)while语句语法
- (36)do-while语句语法
- (37)for语句语法
- (38)break和continue语句
- (39)流程控制综合示例1
- (40)流程控制综合示例2
- (41)流程控制综合示例3
- (42)流程控制综合练习
- (43)数组概述
- (44)数组基础语法
- (45)数组使用示例1
- (46)数组使用示例2
- (47)数组使用示例3
- (48)多维数组基础
- (49)多维数组使用示例1
- (50)多维数组使用示例2
- (51)多维数组练习
- (52)方法声明
- (53)方法声明示例
- (54)方法调用
- (55)方法重载和参数传递
- (56)方法练习
- (57)面向对象基础
- (58)类(一)
- (59)类(二)
- (60)对象
- (61)面向对象设计方法和面向对象特性(一)
- (62)继承(二)
- (63)多态性
- (64)访问控制符、修饰符和其它关键字
- (65)static修饰符
- (66)final修饰符
- (67)this和super
- (68)抽象类和接口(一)
- (69)抽象类和接口(二)
- (70)抽象类和接口(三)
- (71)内部类简介
- (72)包的概念
- (73)JDK文档使用
- (74)java.lang包介绍1
- (75)String类使用
- (76)StringBuffer类和System类
- (77)包装类
- (78)时间和日期处理
- (79)Random随机处理
- (80)集合框架简述
- (81)异常处理概述
- (82)异常处理语法1
- (83)异常处理语法2
- (84)IO简介
- (85)IO类体系
- (86)文件操作之File类使用
- (87)文件操作之读取文件
- (88)文件操作之写文件
- (89)读取控制台输入
- (90)装饰流使用1
- (91)装饰流使用2
- (92)IO使用注意问题
- (93)多线程基础
- (94)多线程实现方式1
- (95)多线程实现方式2
- (96)多线程使用示例1
- (97)多线程使用示例2
- (98)多线程问题及处理1
- (99)多线程问题及处理2
- (100)多线程问题及处理3
- (101)网络编程概述
- (102)网络编程技术1
- (103)网络编程技术2
- (104)网络编程技术3
- (105)网络编程技术4
- (106)网络编程技术5
- (107)网络协议概念
- (108)网络编程示例1
- (109)网络编程示例2
- (110)网络编程小结