欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

面向对象程序设计—继承和多态

程序员文章站 2022-04-21 23:50:28
...

本文为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关键字来定义一个子类,它只允许有一个父类。