1、第5章类的继承第第5章类的继承章类的继承5.1 类的继承类的继承5.2 属性的隐藏和方法的重写属性的隐藏和方法的重写5.3 类之间的层次结构类之间的层次结构5.4 JCreater第5章类的继承第第5章章 类类 的的 继继 承承学习目标学习目标 掌握面向对象程序设计中继承的概念;学会使用继承机制设计Java程序;了解Java语言中类的层次结构;掌握super关键字的使用方法;理解继承机制在软件开发中的优点;掌握JCreater编程工具的基本用法。继承(inheritance)是一种由已有的类创建新类的机制,通过继承可以实现类的复用。继承是面向对象程序设计的主要特征之一。第5章类的继承基本知识基
2、本知识5.1 类类 的的 继继 承承在第1章的案例1-1程序中,定义了一个人类(People),其中定义了人的姓名(name)和年龄(age)两个属性,并定义了一个自我介绍的方法(introduceMyself)。现在,如果要开发一个学生管理系统,就需要定义一个学生类(Student)。我们知道,“学生也是人”,也就是说学生也具有人类的一般特性,如也有姓名和年龄。那么在学生管理系统中,能不能使用案例1-1中定义的人类来构建学生类呢?这就要使用Java程序设计中的继承来实现。本节介绍有关类继承的基本知识。第5章类的继承5.1.1 继承的基本知识继承的基本知识1.继承继承在设计学生管理系统时,定义
3、的学生类(Student)只要继承人类(People)中已经定义的属性和方法,然后根据学生类的特点,添加一些学生所特有的属性和方法即可。这样,就可以简化学生类的定义,因为人类中已经定义好的属性和方法通过继承后,学生类便可以自动拥有。再如,在另外一个系统中,如果已经定义好了一个一般的汽车类,那么其他各种类型的汽车(如轿车、计程车和巴士),都可以从汽车类中继承有关属性和方法,如图5-1所示。第5章类的继承图5-1 汽车类第5章类的继承在图5-1中,汽车类包含了所有汽车具有的公共属性和公共方法,再定义轿车类、计程车类和巴士类时,在这些类中分别添加各自特有的属性和方法,即可完成不同汽车类的定义。图5-
4、1中的汽车类在面向对象程序设计中叫超类、基类或父类。在父类中,通常定义了一些通用的状态与行为。图5-1中,不论轿车、出租车还是巴士,都是汽车,故属于汽车类的一种,这些类都继承了汽车类的属性与行为。在面向对象程序设计中,由继承而得到的类称为子类。第5章类的继承在使用继承机制设计程序时,可以先创建一个具有公有属性的一般类,根据一般类再创建具有特殊属性的新类,新类继承一般类的状态和行为,并根据需要增加它自己特有的状态和行为。因此,可以通过继承实现类的复用,通过类的复用来提高程序设计的效率。注意:在Java语言中,不支持多重继承,即一个子类只能有一个父类。第5章类的继承2.在程序中实现类的继承在程序中
5、实现类的继承在Java程序中,实现类的继承关系要使用extends关键字。通过继承关系定义一个子类的一般格式是:class 子类名 extends 父类名 子类类体创建的子类将会继承父类的属性和方法,此外,我们还可以在子类中添加新的属性和方法。由于Java语言只支持单继承,因此关键字extends后只能写一个父类名。第5章类的继承3.访问控制访问控制一个子类是不是可以访问父类的所有属性和方法呢?当然不是。子类可以访问的父类成员属性和成员方法,决定于父类成员属性和成员方法在定义时所加的访问控制符。Java语言中成员的访问控制共有四种类型,除了前面章节中已经介绍过的public和private修饰
6、符外,还有protected修饰符和默认访问状态。第5章类的继承(1)public修饰符。public是公共的意思,被public修饰的成员可以被所有类访问。(2)private修饰符。类中被private修饰的成员,只能被这个类本身访问,其他类(包括同一个包中的类、其他包中的类和子类)都无法直接访问private修饰的成员。(3)默认访问状态。如果在类的成员前没有任何访问控制符,则该成员属于默认访问状态(default)。处于默认访问状态的成员,只能被同一个包中的其他类访问。因此,默认访问状态的成员具有包可见性。第5章类的继承(4)protected修饰符。如果要让子类能够访问父类的成员,就
7、要把父类的成员声明为protected。另外要注意,用protected修饰的成员也可以被同一个包中的其他类访问。在程序设计中,确定一个成员用什么访问控制符修饰,要视具体情况而定。如果是类中对外提供的接口,就要使用public修饰;如果是不希望被外界访问的成员变量和方法,应当用private修饰;如果是子类中可以访问的成员,则要用protected修饰;如果是该类所在包中的其他类可以访问的成员,则要用默认访问状态(一个成员一般应尽量少用默认访问状态)。对上述访问控制符作用范围的总结如表5-1所示。第5章类的继承表表5-1 访问控制符的作用范围访问控制符的作用范围访问修饰符 被本类访问 被同一包
8、中的 其他类访问 被不同包中 的子类访问 被不同包中的 非子类访问 private 允许 不允许 不允许 不允许 默认访问状态(package)允许 允许 不允许 不允许 protected 允许 允许 允许 不允许 public 允许 允许 允许 允许 第5章类的继承下面通过一个示例说明访问控制符的用法。如图5-2所示,定义了两个包,包名分别为one和two,包one中有一个类E,类E中定义了4个属性,分别用不同的访问控制符修饰。包two中定义了4个类,类A中定义了4个具有不同访问控制符修饰的属性,类B继承了同一个包中的类A,类C继承了不同包中的类E,类D中定义了一个主方法main。第5章类
9、的继承图5-2 访问控制符示例第5章类的继承one包中的类E定义如下:01/定义一个包one02/程序名为E.java03 package one;04 public class E05 private int w=1;06 public int x=2;07 int y=3;08 protected int z=4;09 第5章类的继承two包中定义的类A、B、C和D如下:01/定义一个包two02/程序名为D.java03 package two;04 import one.*;05 06 class A07 private int a=10;08 public int b=20;09 i
10、nt c=30;10 protected int d=40;11 第5章类的继承12 13 class B extends A/类B继承了同一个包two中的类A14 void showA()15/System.out.println(a);/a是父类A的私有成员,子类不能访问16 System.out.println(b);/b是父类A的公共成员,子类可以访问17 System.out.println(c);/c是父类A的默认访问成员,可以被同一个包中的子类访问18 System.out.println(d);/d是父类A的保护成员,子类可以访问19 第5章类的继承21 22 class C
11、extends E/类C继承了不同包中的类E,类E在one包中23 void showE()24/System.out.println(w);/w是父类E的私有成员,子类不能访问25 System.out.println(x);/x是父类E的公共成员,子类可以访问26/System.out.println(y);/y是父类E的默认访问成员,不能被不同包中的子类访问第5章类的继承27 System.out.println(z);/z是父类E的保护成员,子类可以访问28 29 30 class D31 public static void main(String args)32 33 A a1=n
12、ew A();/在类D中实例化一个同包中类A的实例34/System.out.println(a1.a);/a是类A的私有成员,不能被其他类访问35 System.out.println(a1.b);/b是类A的公共成员,可以被其他类访问第5章类的继承36 System.out.println(a1.c);/c是类A的默认访问成员,可以被同一个包中的类访问37 System.out.println(a1.d);/d是类A的保护成员,可以被同一个包中的类访问3839 B b1=new B();40 b1.showA();41 42 C c1=new C();43 c1.showE();44 第5
13、章类的继承45 E e1=new E();/在类D中实例化一个不同包中类E的实例46/System.out.println(e1.w);/w是类E的私有成员,不能被其他类访问47 System.out.println(e1.x);/x是类E的公共成员,可以被其他类访问48/System.out.println(e1.y);/y是类E的默认访问成员,不能被不同包中的类访问49/System.out.println(e1.z);/z是类E的保护成员,不能被不同包中的类访问50 51 第5章类的继承请读者仔细分析程序中语句的注释,用心体会访问控制符的功能。程序中,注释掉了类中不能被访问的其他类的属性
14、。用下列命令分别编译程序E.java和D.java:javac-d.E.javajavac-d.D.java用下列命令执行编译后的程序:java two.D仔细分析程序的运行结果,看与自己分析的结果是否一致。第5章类的继承5.1.2 【案例【案例5-1】定义学生类定义学生类1.案例描述案例描述在开发一个学生管理系统时,要定义一个学生类。学生类除了有姓名和年龄属性外,还有学号和成绩等属性,学生类中要求有定义自我介绍的方法(即要有一个输出学生信息的方法)。2.案例效果案例效果案例程序的执行效果如图5-3所示。第5章类的继承图5-3 案例5-1的显示效果 第5章类的继承3.技术分析技术分析在第1章案
15、例1-1中定义的人类(People)已经有姓名(name)和年龄(age)两个属性,还定义了一个自我介绍的方法(introduceMyself)。因此,在定义学生类(Student)时,可以继承People类,这样只要在Student类中添加学生特有的属性和方法即可。第5章类的继承4.程序解析程序解析下面是学生类的定义,为了测试学生类,在学生类中定义了一个主方法(main)。程序如下:01/*02/案例:5-1 03/程序名:Student.java04/功能:定义学生类 05/*06 第5章类的继承07 class People08 String name;09 int age;10 11
16、void introduceMyself()12 System.out.println(-);13 System.out.println(My name is+name+.);14 System.out.println(I am+age+years old.);15 第5章类的继承16 17 18/学生类19 class Student extends People20 String stuNo;/学号21 int java;/java语言成绩22 23 void introduceMyself()24 System.out.println(-);第5章类的继承25 System.out.pr
17、intln(My name is+name+.);26 System.out.println(I am+age+years old.);27/super.introduceMyself();28 System.out.println(我的学号是+stuNo+.);29 System.out.println(我的Java语言成绩是+java+.);30 31 32 public static void main(String args)第5章类的继承33 Student s1=new Student();34 s1.name=liping;35 s1.age=21;36 s1.stuNo=200
18、70901;37 s1.java=89;38 s1.introduceMyself();3940 第5章类的继承为了便于解释,在该程序代码中将案例1-1中定义的People类也写入了源程序中,其实,当People类和Student类在同一个包中时,源程序中只要定义Student类就可以了。该程序的第19行定义了Student类,该类使用关键字extends继承了People类。在20行和21行定义了学生的两个属性:学号和Java语言成绩。在2330行定义了一个方法introduceMyself,该方法进行学生的自我介绍,其中方法的前3行与People类中定义的introduceMyself方法
19、的功能完全相同。第5章类的继承3239行定义了一个测试Student的主方法,该方法的33行实例化了一个学生类型的对象s1。34行和35行中使用了从父类中继承而来的name属性和age属性。36行和37行中的stuNo属性和java属性则是学生类中定义的新属性。38行调用了在学生类中定义的方法introduceMyself,用来输出学生的信息。第5章类的继承5.1.3 【相关知识】【相关知识】类的修饰符类的修饰符在定义类中的属性和方法时,根据需要可以加访问修饰符。在定义一个类时,根据需要也可以加修饰符。类的修饰符分4种情况:类定义前面没有任何修饰符:这样的类只能被同一个包中的其他类访问,即类具
20、有包(package)访问特性。第5章类的继承 public修饰符:用public修饰的类可以被同一个包中的其他类或不同包的其他类访问。final修饰符:用final修饰的类不允许被继承(在5.3节介绍)。abstract修饰符:用abstract修饰的类不能实例化对象,只能用于继承(在第6章介绍)。第5章类的继承有时需要将public和final组合使用,如在Java API中有一个非常重要的类java.lang.System,该类关系到系统的一些控制信息,如果被子类修改,则有可能造成错误。所以,该类被定义成如下的格式:public final class System extends Ob
21、ject 读者可以查看JDK帮助,System类前面的public表示任何一个类可以访问该类(本书中的每个案例在输出信息时就使用了该类),final表示不能被继承(即该类没有其它子类)。第5章类的继承关于public修饰符要注意以下几个问题:(1)一个源程序文件里,只能有一个被public修饰的类。(2)当一个程序文件中定义了多个类时,文件名必须与public修饰的类名一致,并且文件名中字母的大小写也要与类名一样。(3)一个类要使用其他包中被public修饰的类时,应使用import语句引入该类;也可以使用长类名的方式,如java.awt.Button,表示使用java.awt包中的Butto
22、n类。第5章类的继承(4)其他包中的非公共类是不能使用的。例如,下面是包p1中定义的ClassA类,程序名为ClassA.java(注意ClassA是一个非公共类):package p1;class ClassAint a;第5章类的继承下面是引用p1包中ClassA类的ClassB类(注意,这两个类不在同一个包中),程序名为ClassB.java,ClassB类在包p2中:package p2;import p1.ClassA;class ClassBClassA a;第5章类的继承用下面的命令编译ClassA类:javac d .ClassA.javaClassA类可以被正确编译,然后用下
23、面的命令编译ClassB类:javac d .ClassB.javaClassB类在编译时,出现如下的编译错误:ClassB.java:2:p1.ClassA 在 p1 中不是公共的;无法从外部软件包中对其进行访问import p1.ClassA;第5章类的继承1 错误错误该错误信息的含义是,ClassB.java程序的第2行发生错误,错误的原因是p1.ClassA类不是公共类,不能被不同的包引用。要修改以上编译错误,只要在ClassA的类定义前加public即可。注意:在方法和属性前使用的修饰符private和protected一般不能用在类定义前,也就是说没有私有类和保护类。第5章类的继承
24、5.2 属性的隐藏和方法的重写属性的隐藏和方法的重写子类是一个比父类更具体的类,如“学生是人”和“轿车是车”,这里的“学生”和“轿车”分别是对“人”和“车”的具体化,因此它们比父类“人”和“车”拥有更多的属性和方法,体现在程序设计中,就是一个类在继承父类时,常常要对父类进行如下的扩展:添加新的成员属性(变量);添加新的成员操作(方法);隐藏父类的属性;重写父类中的方法。第5章类的继承5.2.1 属性隐藏和方法重写的基本知识属性隐藏和方法重写的基本知识1.属性的隐藏属性的隐藏如果子类中定义了与父类中同名的属性,则在子类中访问这种属性时,在默认情况下引用的是子类自己定义的成员属性,而将从父类那里继
25、承而来的成员属性“隐藏”起来了。因此,隐藏是指子类对从父类继承来的属性进行了重新定义。子类中,重新定义的属性数据类型可以与父类中的类型相同,也可以与父类中的类型不同。属性的隐藏由于在子类中定义了与父类中同名的属性,因而可能会造成对程序理解上的混乱,况且属性的隐藏在实际软件开发中的用处不大,建议尽量避免使用。第5章类的继承2.方法的重写方法的重写(或叫方法的覆盖或叫方法的覆盖)方法的重写指子类中重新定义了与父类中同名的方法,则父类中的方法被覆盖(Override)。方法的重写通常具有实际意义,如在案例5-1中,学生类的23行重写了与父类中同名的introduceMyself方法,因为学生自我介绍
26、时,不但要介绍普通人具有的姓名和年龄,还要介绍学生所特有的学号和成绩等信息。通过方法重写可以将父类中的方法改造为适合子类使用的方法。被覆盖的方法在子类中访问时,将访问在子类中定义的方法,如案例5-1中的38行,将调用在Student类中定义的introduceMyself方法。第5章类的继承注意1:方法的覆盖需要子类中的方法头和父类中的方法头完全相同,即应有完全相同的方法名、返回值类型和参数列表。注意2:在子类中也可以重载父类中已有的方法。子类中重载的方法应与父类中重载的方法具有相同的方法名和不同的参数形式。注意3:如果不希望子类对从父类继承而来的方法进行重写,则可以在方法名前加final关键
27、字。第5章类的继承注意4:覆盖方法不能比它所覆盖的父类中的方法有更严格的访问权限(访问权限可以相同)。下面举一个实例进行说明。如下的程序中定义了两个类F和S,子类S继承了父类F,F类中定义了一个用protected修饰的属性a和一个用protected修饰的方法showA():01 class F02 protected int a=1;03 protected void showA()04 System.out.println(a);05 第5章类的继承06 07 08 class S extends F09 private int a=2;10 void showA()11 System.
28、out.println(a);12 13 第5章类的继承编译该程序时将出现如下错误:S.java:10:S 中的 showA()无法覆盖 F 中的 showA();正在尝试指定更低的访问权限;为 protected void showA()第5章类的继承1 错误错误该错误发生在showA()方法上。根据本章前面所学的知识我们知道,被protected修饰的成员(第03行的showA()方法)可以“被不同包中的子类访问”,而包访问特性的成员(第10行的showA()方法)只能被同一个包中的类访问,不能“被不同包中的子类访问”。由此,第10行定义的showA()方法比03行定义的showA()方法
29、有“更严格的访问权限”,所以程序出现错误。该错误可按注意4的要求进行修改,即在第10行方法showA前面加public或protected修饰符,使其具有与父类中showA相同或更大的访问控制权限。第5章类的继承但要注意,对于属性没有这样的要求,如子类中第09行的属性a隐藏了父类中02行定义的属性a,父类中的a用protected修饰,而子类中的a用private修饰,这是允许的。注意5:覆盖方法不能比它所覆盖的方法抛出更多的异常(第7章介绍)。第5章类的继承3.子类访问父类被重写的方法和被隐藏的属性子类访问父类被重写的方法和被隐藏的属性要在子类中访问被重写的方法和被隐藏的属性时,如果直接用被
30、重写的方法名或被隐藏的属性名,则被访问的是子类中定义的方法和属性。如果要在子类中访问父类中被重写的方法和被隐藏的属性,则要使用关键字super。第5章类的继承简单地说,super表示当前对象的直接父类对象。因此,可以通过super关键词访问到父类中被重写的方法和被隐藏的属性。super的使用有三种情况:(1)访问父类中被隐藏的成员变量,如:super.变量名;(2)调用父类中被重写的方法,如:super.方法名(参数列表);第5章类的继承(3)调用父类的构造方法,如:super(参数列表);在案例5-1中,父类People中定义的introduceMyself方法可以输出一个人的姓名和年龄,因
31、此子类中23行定义的introduceMyself方法,可以调用父类中11行已经定义好的introduceMyself方法。具体做法是将程序中的第24行、25行和26行加上注释,而将27行的注释去掉,程序将得到相同的执行结果。第5章类的继承在下面的程序片段中,第6行使用super.x访问父类中被隐藏的属性x:1 class X2 int x=1;3 4 5 class Y extends X6 int x=super.x+2;7 如何使用super调用父类的构造方法,将在下一节介绍。第5章类的继承5.2.2 【案例【案例5-2】重写学生类重写学生类1.案例描述案例描述本节将按照面向对象的程序设
32、计思想重写案例5-1的学生类。2.案例效果案例效果见案例5-1的显示效果。3.技术分析技术分析在定义人类Person时,子类有权访问的属性应该用protected修饰;一个类的对外接口(即其他类可以访问的方法)应该用public;子类在继承父类时,根据需要可以扩展某些方法的功能,即对某些方法进行覆盖。第5章类的继承4.程序解析程序解析下面是案例5-2的程序代码:01/*02/案例:5.2 03/程序名:Student2.java04/功能:定义学生类 05/*06 07 class People208 protected String name;09 protected int age;第5章
33、类的继承10 11 public People2()12 13 public People2(String name,int age)14 this.name=name;15 this.age=age;16 17 18 public void introduceMyself()19 System.out.println(-);20 System.out.println(My name is+name+.);第5章类的继承21 System.out.println(I am+age+years old.);22 23 24 25/学生类26 class Student2 extends Peop
34、le227 private String stuNo;/学号28 private int java;/java语言成绩29 30 public Student2(String name,int age,String stuNo,int java)31 this.name=name;第5章类的继承32 this.age=age;33 this.stuNo=stuNo;34 this.java=java;35 36 37 public void introduceMyself()38 super.introduceMyself();39System.out.println(我的学号是+stuNo+
35、.);第5章类的继承40System.out.println(我的Java语言成绩是+java+.);414243 public static void main(String args)44Student2 s1=new Student2(liping,21,20070901,89);45s1.introduceMyself();4647该程序比较简单,请读者自己分析。第5章类的继承5.2.3 【相关知识】【相关知识】方法覆盖与方法重载的区别方法覆盖与方法重载的区别“覆盖”与“重载”两种技术对Java语言的初学者而言很容易混淆。所谓“重载”(Overload),是在同一个类(或父类与子类)中
36、定义了名称相同但参数个数或参数类型不同的方法,因此在调用方法时,系统便可依据参数的个数或类型来决定调用哪一个被重载的方法。所谓“覆盖”(Override),是在子类中定义了名称、参数个数与类型均与父类相同的方法,用以改写或扩展父类里方法的功能。第5章类的继承下面通过一个实例来说明方法重载与方法覆盖的区别:01 class F02 protected void showA()03 System.out.println(F类中的showA方法);04 05 06 07 class S extends F08 protected void showA()09 System.out.println(S
37、类中的showA方法);10 第5章类的继承11 12 void showA(int a)13 System.out.println(a);14 15 16 public static void main(String args)17 new S().showA(3);18 new S().showA();19 20 第5章类的继承父类F中02行定义的showA方法与子类S中08行定义的showA方法是覆盖关系,而子类S中12行定义的showA方法与其中08行定义的showA方法是重载关系,因为它们的参数不同。第5章类的继承5.3 类之间的层次结构类之间的层次结构一个子类继承了一个父类,同时该
38、子类还可以是其他类的父类,这样就形成了一个类之间的继承关系图。本节介绍类层次关系图,以及父类对子类的影响等问题。第5章类的继承5.3.1 类的层次结构类的层次结构类A如果有两个子类AA和AB,类AA如果有一个子类AAA,类AB如果有一个子类ABA,它们之间的继承关系就构成了图5-4左边的部分。类似地,由B类及其子类构成了图5-4中间的部分,其他类之间也可以构成类似的关系。第5章类的继承图5-4 类的层次结构第5章类的继承1.Object类类图5-4所示的树形结构中,类是分层次的,最顶层是Object类。在Java语言中,所有类都直接或间接地继承了在Java API中定义的Object类,Obj
39、ect类位于java.lang包中。在一个类定义中,如果没有直接指出其父类,则Java语言默认其父类为Object。例如,下面定义了一个类Point:class Point float x;float y;第5章类的继承它与下面Point的定义是等价的:class Point extends Object float x;float y;Java语言中,所有类都是由Object类导出的,即所有类都直接或间接地继承了Object类。因此,在Object类中定义的public方法可以被任何一个Java类使用,也就是说,任何一个Java对象都可以调用这些方法。Object类中常用的两个实例方法是:第
40、5章类的继承 equals():equals方法等价于比较运算符(=)。比较运算符用来比较两个简单数据类型的值是否相等,或者判断两个对象是否具有相同的引用值。toString():该方法返回代表这个对象的一个字符串表示。默认情况下,返回的字符串由该对象所属的类名、符号和代表该对象的一个数组成。例如:System.out.println(myClinder),输出类似Cylinder15037e5的字符串,这些信息的意义不大,在编程中通常重写toString方法,使它返回一个代表该对象的易懂的字符串。第5章类的继承分析下面的示例程序(注意equals()和toString()在程序中的用法):0
41、1 class Obj02 int x=12;03 04 05 class TestObj06 public static void main(String args)第5章类的继承07 Obj a=new Obj();08 Obj b=new Obj();09 Obj c=b;/c和b引用内存中相同的对象10 System.out.println(a.toString();11 System.out.println(a);12 System.out.println(a.equals(b);13 c.x=24;14 System.out.println(b.equals(c);15 Syste
42、m.out.println(b.x);16 17 该程序执行后输出的结果如图5-5所示。第5章类的继承图5-5 示例程序的运行结果第5章类的继承从图5-5中可以看出,如果调用含有对象参数的System.out.println方法(即输出的量为对象名称),则系统就会自动调用toString方法打印出相应的信息。第5章类的继承2.继承的传递性继承的传递性继承具有传递性。也就是说,父类可以把一些特性传递给子类,而子类又可以把这些特性再传递它的子类(即子类的子类),依次类推。因此,子类继承的特性可能来源于它的父类,也可能来源于它的祖先类。分析下面的示例程序。01 class A02 public in
43、t a1=1;03 int a2=2;04 protected int a3=3;第5章类的继承05 06 07 class B extends A /B类继承了A类08 public int b1=11;09 int b2=22;10 protected int b3=33;11 12 13 class C extends B /C类继承了B14 int c1=111;15 void showC()第5章类的继承16 System.out.println(clas A a1=+a1);17 System.out.println(clas A a2=+a2);18 System.out.pri
44、ntln(clas A a3=+a3);19 System.out.println(clas B b1=+b1);20 System.out.println(clas B b2=+b2);21 System.out.println(clas B b3=+b3);22 System.out.println(clas C c1=+c1);第5章类的继承23 24 25 public static void main(String args)26 C objC=new C();27 objC.showC();28 29 该程序的执行结果如图5-6所示。分析图5-6中输出的结果,弄清类之间的继承关系。
45、第5章类的继承图5-6 示例程序的执行结果第5章类的继承3.对象的初始化对象的初始化在Java语言中,对象的初始化非常重要。在使用对象之前,首要先初始化对象,这样做是为了保证对象处于安全状态。一个子类对象既包含了从父类继承来的属性,也包含了它自己新定义的属性,因此在创建一个子类对象时,这些属性都要被正确的初始化。第5章类的继承其中,参数是可选的。如果没有参数,则super()调用的就是父类无参的构造方法。如果子类的构造方法中没有调用父类构造方法的super语句,则系统就会在子类构造方法执行时,自动调用父类无参构造方法,即执行一条super()语句。该规则的用意是确保在子类构造方法执行之前,就调
46、用了父类的构造方法,以完成对父类变量的初始化操作。尽管子类的构造方法可以自动调用父类的无参构造方法,但是为了养成良好的编程习惯,作者建议在编写子类的构造方法时,第一条语句应该是调用父类构造方法的语句。第5章类的继承根据以上说明,请读者自己分析下面示例程序的运行结果:01 class Art02 Art()/Art类的构造方法03 System.out.println(Art Constructor.);04 05 06 07 class Drawing extends Art第5章类的继承08 Drawing()/Drawing类的构造方法09 System.out.println(Drawi
47、ng Constructor.);10 11 12 public static void main(String args)13 Drawing d=new Drawing();/创建一个Drawing类的对象14 15 第5章类的继承分析下面的程序有什么错误:01 class Employee 02 String name;03 /public Employee()04 public Employee(String n)05 name=n;06 07 08 09 class Manager extends Employee 第5章类的继承10 String department;11 /pu
48、blic Manager()12 public Manager(String s,String d)13 super(s);/调用父类带一个参数的构造方法14 department=d;15 16 第5章类的继承17 18 class TestManager19 public static void main(String args)20 Manager m1=new Manager();21 Manager m2=new Manager(ZhangFan,market);22 23 第5章类的继承程序的20行调用Manager类的默认构造方法初始化一个对象m1,但由于在Manager类中已经
49、定义了一个带参数的构造方法,系统就不会自动添加一个无参的默认构造方法,因此程序编译时将发生错误。如果给Manager类添加一个无参的构造方法,如第11行所示(将11行的注释去掉),程序编译时还会发生错误,这又是为什么呢?因为Manager类中已经添加了无参的构造方法 第5章类的继承该构造方法要自动调用其父类Employee的无参构造方法,而父类Employee中没有定义无参的构造方法,并且由于在类Employee中已经定义了一个有参数的构造方法,因此系统也不会自动添加一个无参的构造方法。为了使该程序能被正确编译,还要给Employee类添加一个无参的构造方法,即将03行的注释去掉后编译程序,则
50、不会发生编译错误。这个错误是初学者最常见的一个错误,防范的措施就是给每一个类都加上无参的构造方法。第5章类的继承注意1:父类的构造方法的功能是完成父类属性的初始化,因此子类不能继承父类的构造方法。注意2:用super调用父类的构造方法时,该语句只能出现在子类构造方法的第一行。由于在一个类的构造方法中使用this(见第3章)调用该类的其他构造方法时,也要将其放在第一行,因此在一个构造方法中,this调用和super调用不能同时出现(请读者自己分析其中的原因)。第5章类的继承最后,将创建一个类的对象时对象初始化的顺序总结如下:首先,将分配到的存储空间自动进行初始化(初始化的值,见表3-7所示)。其