5.1 继承简介
在面向对象程序设计中,继承是不可或缺的一部分。通过继承可以实现代码的重用,提高程序的可维护性。
5.1.1 继承的概念
继承一般是指晚辈从父辈那里继承财产,也可以说是子女拥有父母所给予他们的东西。在面向对象程序设计中,继承的含义与此类似,不同的是,这里继承的财产是类。也就是说,继承是子类拥有父类的成员。
在动物园中有许多动物,这些动物具有相同的属性和行为。这时可以编写一个动物类Animal(该类中包括所有动物均具有的属性和行为),即父类。但是不同类的动物又具有它自己特有的属性和行为。例如,鸟类具有飞的行为,这时可以编写一个鸟类Bird,由于鸟类属于动物类,它也具有动物类所共同拥有的属性和行为,因此在编写鸟类时,就可以使Bird 类继承父类Animal。这样不但可以节省程序的开发时间,而且提高了代码的可重用性。Bird 类与Animal 类的继承关系如图5.1 所示。
图5.1 Bird 类与Animal 类的继承关系
5.1.2 子类对象的创建
在类的声明中,可以通过使用关键字extends 来显式地指明其父类。
语法格式如下:
●修饰符:可选参数,用于指定类的访问权限,可选值为public、abstract 和final。
●子类名:必选参数,用于指定子类的名称。类名必须是合法的Java 标识符,在一般情况下,要求首字母大写。
●extends父类名:必选参数,用于指定要定义的子类继承于哪个父类。
例如,定义一个Cattle 类,该类继承于父类Animal,即Cattle 类是Animal 类的子类,具体代码如下:
5.1.3 继承的使用原则
子类可以继承父类中所有可被子类访问的成员变量和成员方法,但必须遵循以下原则。
(1)子类能够继承父类中被声明为public 和protected 的成员变量和成员方法,但不能继承被声明为private 的成员变量和成员方法。
(2)子类能够继承在同一个包中的由默认修饰符修饰的成员变量和成员方法。
(3)如果子类声明了一个与父类的成员变量名称相同的成员变量,则子类不能继承父类的成员变量。此时子类的成员变量隐藏了父类的成员变量。
(4)如果子类声明了一个与父类的成员方法名称相同的成员方法,则子类不能继承父类的成员方法。此时子类的成员方法覆盖了父类的成员方法。
【例5.1】 定义一个动物类Animal 及它的子类Bird。
(1)创建一个名称为Animal 的类,并在该类中声明一个成员变量live 和两个成员方法,分别为eat() 和move(),具体代码如下:
(2)创建一个Animal 类的子类Bird,在该类中隐藏父类的成员变量skin,并且覆盖成员方法move(),具体代码如下:
(3)创建一个名称为Zoo 的类,在该类的main() 方法中创建子类Bird 的对象并为该对象分配内存,然后使该对象调用该类的成员方法及成员变量,具体代码如下:
eat() 方法是从父类Animal 继承的方法,move() 方法是子类Bird 声明的覆盖父类成员方法的成员方法,skin 变量是子类的成员变量。程序运行结果如图5.2 所示。
图5.2 Bird 类继承Animal 类
5.1.4 关键字super
子类可以继承父类的非私有成员变量和方法(不是以关键字private 修饰的),但是,如果子类中声明的成员变量与父类的成员变量同名,则父类的成员变量将被隐藏。如果子类中声明的成员方法与父类的成员方法的名称相同,并且参数个数、类型和顺序也相同,则称子类的成员方法覆盖了父类的成员方法。这时,如果想要在子类中访问父类中被子类隐藏的成员方法或变量,就可以使用关键字super。
关键字super 主要有以下两种用途。
(1)调用父类的构造方法。
子类可以调用父类的构造方法,但是必须在子类的构造方法中使用关键字super 来调用。语法格式如下:
如果父类的构造方法中包括参数,则参数列表为必选项,用于指定父类构造方法的入口参数。
例如,在Animal 类中添加一个默认的构造方法和一个带参数的构造方法,具体代码如下:
这时,如果想要在子类Bird 中使用父类的带参数的构造方法,则需要在子类Bird 的构造方法中通过以下代码进行调用:
(2)操作被隐藏的成员变量和被覆盖的成员方法。
如果想要在子类中操作父类中被隐藏的成员变量和被覆盖的成员方法,可以使用关键字super。
语法格式如下:
如果想要在子类Bird 的成员方法中改变父类Animal 的成员变量skin 的值,可以使用以下代码:
如果想要在子类Bird 的成员方法中使用父类Animal 的成员方法move(),可以使用以下代码: