(未完待续)
五、子类与继承
5.1 子类与父类
继承是一种由已有的类创建新类的机制。利用继承,我们可以先创建一个共有属性的一般类,根据该一般类再创建具有特殊属性的新类,新类继承一般类的状态和行为,并根据需要增加它自己的新的状态和行为。由继承而得到的类称为子类,被继承的类称为父类(超类)。
Java不支持多重继承(子类只能有一个父类)。
5.1.1 声明子类
使用关键字extends来定义一个类的子类,格式如下:
class 子类名 extends 父类名 {
…
}
例如:
class Student extends People {
…
}
说明:把Student类定义为People类的子类、People类是Student类的父类
5.1.2 类的树形结构
Java的类按继承关系形成树形结构这个树形结构中,根节点是Object类(Object是java.lang包中的类),即Object是所有类的祖先类。
除了Object类,每个类都有且仅有一个父类,一个类可以有多个或零个子类。如果一个类(除了Object类)的声明中没有使用extends关键字,这个类被系统默认为是Object的子类,即类声明“class A”与“class A extends Object”是等同的。
5.2 子类的继承性
类可以有两种重要的成员:成员变量和方法。子类的成员中有一部分是子类自己声明定义的,另一部分是从它的父类继承的。
所谓子类继承父类的成员变量就是把继承来的变量作为自己的一个成员变量,就好象它们是在子类中直接声明一样,可以被子类中自己定义的任何实例方法操作。
所谓子类继承父类的方法就是把继承来的方法作为子类中的一个方法,就好象它们是在子类中直接定义了一样,可以被子类中自己定义的任何实例方法调用。
class Father{
float weight,height;
String head;
void speak(String s){
System.out.println(s)
}
}
class Son extends Father{
String head,foot;
}
Son s = new Son();
5.2.1 子类和父类在同一包中的继承性
如果子类和父类在同一个包中,那么,子类自然地继承了其父类中不是private的成员变量作为自己的成员变量,并且也自然地继承了父类中不是private的方法作为自己的方法,继承的成员变量或方法的访问权限保持不变。
下面的例子1中有4个类:People,Student.java,UniverStudent.java和Example5_1,这些类都没有包名(需要分别打开文本编辑器编写、保存这些类的源文件,比如保存到C:\ch5目录中),其中UniverStudent类是Student的子类,Student是People的子类。程序运行效果如图5.1。
package Example5_1;
public class People {
int age,leg = 2,head = 2;
protected void showPeopleMess(){
System.out.printf("%d岁,%d腿,%d头",age,leg,head);
}
}
package Example5_1;
public class Student extends People{
int number;
public void tellNumber(){
System.out.printf("%d\t",number);
}
int add(int x,int y){
return x+y;
}
}
package Example5_1;
public class University extends Student{
int multi(int x,int y){
return x*y;
}
}
package Example5_1;
public class Example5_1 {
public static void main(String args[]){
Student zhang = new Student();
zhang.age = 21;
zhang.head = 1;
zhang.leg = 2;
zhang.number = 1312;
zhang.tellNumber();
University kong = new University();
kong.age = 22;
kong.head = 1;
kong.leg = 2;
kong.number = 1314;
System.out.println("kong可以做加法运算"+kong.add(10,2));
System.out.println("kong可以做乘法"+kong.multi(10,2));
}
}
5.2.2 子类和父类不在同一包中的继承性
如果子类和父类不在同一个包中,那么,子类继承了父类的protected、public成员变量做为子类的成员变量,并且继承了父类的protected、public方法为子类的方法,继承的成员或方法的访问权限保持不变。
5.2.3 继承关系(GENERALIZATION)的UML图
如果一个类是另一个类的子类,那么UML通过使用一个实线连接两个类的UML图来表示二者之间的继承关系,实线的起始端是子类的UML图,终点端是父类的UML图,但终点端使用一个空心的三角形表示实线的结束。
5.3 子类与对象
类继承了父类的很多东西,那么子类在创建对象的时候,他又是怎么生成自己的对象的呢。子类生产的对象会有哪些东西呢。
//例题:子类的继承
class Father
{ float weight,height;
String head;
void speak(String s)
{ System.out.println(s);
}
}
class Son extends Father
{ String hand,foot;
}
Son s=new Son();
5.3.1 子类对象的生成
子类创建对象时,子类的构造方法总是先调用父类的某个构造方法,完成父类部分的创建;然后再调用子类自己的构造方法,完成子类部分的创建。如果子类的构造方法没有明显地指明使用父类的哪个构造方法,子类就调用父类的不带参数的构造方法 。
子类在创建一个子类对象时,不仅子类中声明的成员变量被分配了内存,而且父类的所有的成员变量也都分配了内存空间,但子类只能操作继承的那部分成员变量 。
子类可以通过继承的方法来操作子类未继承的变量和方法 .
例子2中,子类ChinaPeople的对象调用继承的方法操作未被子类继承却分配了内存空间的变量。程序运行效果如图5.3。
package Example5_2;
class People{
private int averHeight = 166;
public int getAverHeight(){
return averHeight;
}
}
class ChinaPeople extends People{
int height;
void setHeight(int h){
height = h;
}
public int getheight(){
return height;
}
}
public class EXample5_2 {
public static void main(String args[]){
ChinaPeople zhangsan = new ChinaPeople();
System.out.println("子类未继承的averHeight是"+zhangsan.getAverHeight());
zhangsan.setHeight(20);
System.out.println("子类的实例变量的值是"+zhangsan.height);
System.out.println("子类的实例变量的值是"+zhangsan.getheight());
}
}
结论
1)创建子类对象时,子类总是按层次结构从上到下的顺序调用所有超类的构造函数。
2)如果父类没有不带参数的构造方法,则在子类的构造方法中必须明确的告诉调用父类的某个带参数的构造方法,通过super关键字,这条语句还必须出现在构造方法的第一句。
5.3.2 关于INSTANCEOF运算符
instanceof运算符是Java独有的双目运算符,其左面的操作元是对象,右面的操作元是类,当左面的操作元是右面的类或其子类所创建的对象时,instanceof运算的结果是true,否则是false 。请看例题。
class Test
{ String s;
Test()
{ stu s=new String();
if(s instanceof String)
{ System.out.println(“YES”);
}
}
}
5.4 成员变量的隐藏和方法重写
5.4.1 成员变量的隐藏
对于子类可以从父类继承的成员变量,只要子类中声明的成员变量和父类中的成员变量同名时,子类就隐藏了继承的成员变量。
在子类中要操作这个与父类同名的成员变量时,子类操作的是子类重新声明的这个成员变量。而不是被隐藏掉的。,
例子3(Example5_3.java)中,Goods类有一个名字为weight的double型成员变量,本来子类CheapGoods可以继承这个成员变量,但是子类CheapGoods又重新声明了一个int型的名字为weight的成员变量.程序运行效果如图5.4
package Example5_3;
public class Goods {
public double weight;
public void oldSetweight(double w){
weight = w;
System.out.println("double型的weight="+weight);
}
public double getWeight(){
return weight;
}
public double oldGetprice(){
double price = weight * 10;
return price;
}
}
package Example5_3;
public class CheapGoods extends Goods{
public int weight;
public void newSetweight(int w){
weight = w;
System.out.println("int型的weight是"+weight);
}
public double newGetPrice(){
double price = weight * 10;
return price;
}
}
package Example5_3;
public class Example5_3 {
public static void main(String args[]){
CheapGoods cheapgoods = new CheapGoods();
cheapgoods.newSetweight(198);
System.out.println("对象cheapgoods的weight的值是:"+cheapgoods.weight);
System.out.println("对象cheapgoods的价格是:"+cheapgoods.newGetPrice());
cheapgoods.oldSetweight(100.74);//利用子类对象调用继承的方法操作隐藏的double型变量weight
System.out.println("使用老的方法计算出的价格是:"+cheapgoods.oldGetprice());
}
}
5.4.2 方法重写(OVERRIDE)
同样,子类通过重写可以隐藏已继承的实例方法。
1.重写的语法规则 如果子类继承了父类的实例方法,那么子类就有权利重写这个方法。 方法重写是指:子类中定义一个方法,这个方法的类型和父类的方法的类型一致或是父类方法的类型的子类型,且这个方法的名字、参数个数、参数的类型和父类的方法完全相同.
2.重写的目的 子类通过方法的重写可以隐藏继承的方法,子类通过方法的重写可以把父类的状态和行为改变为自身的状态和行为。
3. 重写后方法的调用 子类创建的一个对象,如果子类重写了父类的方法,则运行时系统调用的是子类重写的方法; 子类创建的一个对象,如果子类未重写父类的方法,则运行时系统调用的是子类继承的方法;
4.重写的注意事项 重写父类的方法时,不允许降低方法的访问权限,但可以提高访问权限(访问限制修饰符按访问权限从高到低的排列顺序是:public、protected、友好的、private。)
package EXample5_4;
public class University {
void enterRule(double math,double chinese,double english){
double avrScore = (math + chinese + english)/3;
if(avrScore >= 60){
System.out.println(avrScore+"达到相应的要求");
}
else System.out.println(avrScore+"未达到相应的要求");
}
}
package EXample5_4;
public class ImportantUniversity {
public void enterrule(double math,double chinese,double english){
double avrScore =(math + chinese + english)/3;
if(avrScore >= 73) System.out.println(avrScore+"通过了重点大学的录取的分数线");
else System.out.println(avrScore+"未通过重点大学的录取的分数线");
}
}
package EXample5_4;
public class Example5_4 {
public static void main(String args[]){
ImportantUniversity zhangsan = new ImportantUniversity();
zhangsan.enterrule(50,100,120);
}
}