面向对象程序设计—继承和多态
本文为Java语言程序设计中面向对象程序设计的分篇。主要由个人用于在Java语言程序设计,尤其是面向对象程序设计方面的学习理解,方便回顾总结。
采用书籍为 Y.Daniel Liang 所著的《Java语言程序设计-基础篇》第八版。
继承和多态
我们知道,面向对象程序设计的三个特点是封装、继承和多态,在这里我们先来学习继承。
什么是继承?继承就是从已有的类A中派生出新的类B,类B除了有自己特有的属性之外,还包含了类A中的所有属性。
通过继承,我们可以避免设计类时造成冗余,并且可以使得程序更易于理解和维护。
1.1 父类和子类
在Java的术语中,如果类C1拓展自另一个类C2,那么就将C1称为次类,将C2称为超类。超类也称为父类或基类,次类又称为子类、拓展类或派生类。
我们通过举例来进一步理解父类和子类。
假设我们在平面设计建模时需要创建像圆、矩形这样的几何对象,这些几何对象有一些共同的属性和行为 —— 它们可以是用某种颜色画出来的,并且可以选择是否填充。 那么此时,我们可以设计一个通用类 GeometricObject 并用它来建模所有的几何对象,这个类包含了属性color和 illed,以及适用于这些属性的get和 set 方法。但是这些几何对象除了这些共同的属性之外,还有各自的形状、大小。因此我们需要通过拓展GeometricObject类来定义Circle类或者Rectangle类或者其它几何图形类。此时,我们称 GeometricObject 类为 Circle 的父类,Circle类为GeometricObject类的子类。
子类继承了它父类的属性的同时,也从它的父类中继承了可访问的数据域和方法,同时还可以添加新数据域和新方法。
如图11-1所示,Circle类继承了GeometricObject类所有可以访问的数据域和方法。除此之外,它还有一个新的数据域radius,以及与radius相关的get和set方法。它还包括getArea()、getPerimeter()等方法以返回圆的面积、周长。
基本的概念我们已经了解了,然后我们通过代码来进一步学习继承,以下是GeometricObject类以及Circle类的代码。
//GeometricObject.java
public class GeometricObject{
private String color = "white";
private boolean filled;
private Java.util.Date dateCreated;
//创建一个默认的几何对象
public GeometicObject(){
dateCreated = new java.util.Date();
}
// 使用指定的颜色和填充值构造一个几何对象
public GeometricObject(String color,boolean filled){
dateCreated = new java.util.Date();
this.color = color;
this.filled = filled;
}
// 返回该几何图形的颜色
public String getColor(){
return color;
}
// 设置一个新的颜色
public void setColor(String color){
this.color = color;
}
// 返回该几何图形的填充值
public boolean isFilled(){
return filled;
}
// 更改填充值
public void setFilled(boolean filled){
this.filled = filled;
}
// 获得这个几何图形
public java.util.Date getDateCreated(){
return dateCreated;
}
// 对toString重写 返回该几何图形的字符串表示形式
public String toString(){
return "created on" + dateCreated + "\ncolor" + color+
" and filled" + filled;
}
}
// Circle.java
public class Circle extends GeometricObject{
private double radius;
// 创建一个默认的圆(这个默认包括Circle类的属性以及GeometricObject类的属性)
public Circle(){
}
// 更改圆的半径
public Circle4(double radius){
this.radius = radius;
}
// 更改圆的半径、颜色以及填充值
public Circle(double radius, String color, boolean filled){
this.radius = radius;
setColor(color);
setFilled(filled);
}
// 返回圆的半径
public double getRadius(){
return radius;
}
// 更改圆的半径
public void setRadius(double radius){
this.radius = radius;
}
// 返回圆的面积
public double getArea(){
return radius * radius * Math.PI;
}
// 返回圆的直径
public double getDiameter(){
return 2 * radius
}
// 返回圆的周长
public double Perimeter(){
return 2 * radius * Math.PI;
}
// 输出圆的信息
public void printCircle(){
System.out.println("The circle is created " + gerDateCreated() +
" and the radius is " + radius );
}
}
Circle类代码的第一行关键字extends告诉编译器,Circle类拓展GeometricObject类,这样,它就继承了getColor、setColor、isFilled、setFilled和toString方法。
重载的构造方法Circle(double radius, String color,boolean filled)是通过调用getColor和setFilled方法设置color和filled属性来执行的。这两个公共方法是在父类GeometricObject中定义的,在Circle中继承。因此,可以在子类中使用它们。
可以尝试在构造方法中使用数据域color和filled,如下所示:
public Circle(double radius, String color, boolean filled){
this.radius = radius;
this.color = color; // Illegal(非法的)
this.filled = filled; // Illegal
}
这是错误的,因为GeometricObject类中的私有数据域color和filled是不能被除了GeometricObject类本身之外的其他任何类访问的。而唯一读取和改变color与filled的方法就是通过它们的get和set方法。
下面是在考虑继承时很值得注意的几点:
(1):和传统的理解相反,子类并不是父类的一个子集。实际上,一个子类通常比它的父类包含更多的信息和方法。
(2):父类中的私有数据域在该类之外是不可访问的,因此不能在子类中直接使用它们。
(3):不是所有的“是关系”都该用继承来建模。例如:一个正方形是一个矩形,但不应该定义一个Square类来拓展Rectangle类,因为没有任何东西能从矩形拓展到正方形。如果要用类B去拓展类A,那么A应该比B包含更多的信息。
(4):继承是用来为“是关系”建模的,不应该盲目地拓展一个类。即使Person类和Tree类可以共享高度和重量这样的通用特性,但从Person类拓展出Tree类是毫无意义的。一个父类和它的子类之间必须存在是关系。
(5):在Java中不允许多重继承,一个Java类只可能直接继承自一个父类,这种限制被称为单一继承。如果使用extends关键字来定义一个子类,它只允许有一个父类。
上一篇: SpringMVC 全局异常处理
下一篇: 反射学习(敲下代码)