1第五章对类的进一步讨论本章提要1.继承的概念。2.子类与父类的关系:is-a关系。3.访问控制符protected。4.关键字super。5.成员的隐藏和方法的重构。6.子类对象的使用。7.抽象类。8.接口与实现。9.包10.JAVA类库简介11.关于类Object25.1继承5.1.1继承的基本概念继承是面向对象编程领域中的另一个非常重要的基本概念。继承的思想就是:允许在某个原有类的基础上,通过增添新的方法和成员,以及修改原有类中的方法和成员,来产生一个新的类,以适合新的应用需要。通常,我们将这个原有类称为“父类”(或称为“超类”或“基类”),而这个新类则被称为“子类”。也就是说,子类通过继承父类,不但可以全盘复制(即继承)父类中的成员和方法,同时还能修改父类并且增添新的成员和方法。“继承”这项技术,对于JAVA编程来说是非常优秀和重要的。35.1.2子类的定义子类的定义格式为:[public]class子类名extends父类名{成员及方法声明}其中,父类名代表一个原有类,子类名则代表当前所要定义的新类。下面我们就利用类Employee作为父类来定义一个名为Manager的子类。我们知道,类Employee是一个用于描述公司职员的类。然而,对于公司经理来说,如果他完成了约定的任务,那么,他还会得到一笔额外的奖金。实际上,经理仅比职员多了有关奖金的数据及其处理。当然,你可以为经理重新定义一个类Manager。然而,你现在却可以利用继承类Employee的方式来定义类Manager:你只需声明新增加的一个存放奖金的成员和一个处理奖金的方法。至于其他的域,你将自动拥有(及继承)类Employee所有的域,而无需再重复声明!4例5-1定义类Manager(Employee为其父类)classEmployee//雇员类(作为父类){privateStringname;privatedoublesalary;privateintid;privatestaticintnextId=1;Employee(Stringn,doubles){name=n;salary=s;id=nextId++;}StringgetName(){returnname;}doublegetSalary(){returnsalary;}intgetId(){returnid;}}classManagerextendsEmployee//经理类(Employee的子类){privatedoublebonus;//声明成员“奖金”voidsetBonus(doubleb)//声明方法“设置奖金”{bonus=b;}voidgetBonus()//声明方法“提取奖金”{returnbonus;}}5现在,我们先来看一看类Manager及类Employee的存储结构:类Manager类EmployeeprivatenameprivateidprivatenextId=1Employee()getName()getSalary()getId()privatebonussetBonus()getBonus()privatenameprivateidprivatenextId=1Employee()getName()getSalary()getId()全盘继承当从一个父类来定义一个子类时,你只需说明它们的区别即可!显然,一个子类至少不会比父类小!65.1.3子类与父类的关系其实,在类Manager与类Employee之间,有一个明显的“is-a”关系。也就是说:Manager“is-a”Employee。即,每一个经理都是一个职员,而每个职员并不都是经理。当从一个父类定义一个子类时,用户实际上是在进行一个具体化的过程。它向更接近新的应用需求迈进了一步,这也叫特化。特化有多种形式:1)添加新的行为。2)改变原有的行为。(稍后介绍)以后我们会进一步看到,类的这种关系还能呈现出下面的性质:在所有需要父类出现的地方,都可以由子类去代替。因为,子类是一个父类(即,“is-a”的关系)。这种性质被称为对象的多态性。注意:“is-a”这个概念来源于“集合论”。所有职员的集合包含所有经理的集合。可以认为“职员集合是经理集合的父类”,而“经理集合是职员集合的子类”。7在类Manager中定义的这些域没有什么特别之处。如果你有一个经理对象,如,Managerboss=newManager();那么,你可以简单地使用以下语句:boss.setBonus(5000);//调用类Manager新增的方法“设置奖金”调用从父类继承过来的方法并且,你还可使用下面这条语句:System.out.println(boss.getName()+”的总收入是:”+boss.getSalary()+boss.getBonus());这是因为,继承已经使得子类自动“拥有”了父类的所有域!但是,如果你有一个Employee对象,如,Employeee1=newEmployee(“John”,2000);那么,你肯定不能调用以下方法:e1.setBonus(3000);//类Employee的对象去调用类Manager的方法。原因很简单——该方法setBonus没有在类Employee中定义!85.1.4对继承的规定(1)尽管子类自动拥有(即继承)了父类的所有域(包括它们的访问权限),但子类中的新方法却不能直接访问父类的私有域,而只能通过调用父类的方法才可实现。privatenameprivateidprivatenextId=1Employee()getName()getSalary()getId()privatenameprivateidprivatenextId=1Employee()getName()getSalary()getId()privatebonussetBonus()getBonus()类Employee类Manager可直接访问类Manager类Employee可直接访问注意:你不应该将类中的方法声明为私有的!否则,该类将无继承的价值!9(2)在子类的构造方法中,不能直接对父类的成员进行初始化。但可使用关键字super来调用父类的构造方法,以完成对父类中成员的初始化。例5-2在类Manager定义中声明构造方法classEmployee//雇员类{privateStringname;privatedoublesalary;privateintid;privatestaticintnextId=1;Employee(Stringn,doubles){name=n;salary=s;id=nextId++;}StringgetName(){returnname;}doublegetSalary(){returnsalary;}intgetId(){returnid;}}classManagerextendsEmployee//经理类{privatedoublebonus;//声明成员“奖金”Manager(Stringn,doubles,doubleb)//声明构造方法{super(n,s);bonus=b;}voidsetBonus(doubleb)//声明方法“设置奖金”{bonus=b;}voidgetBonus()//声明方法“提取奖金”{returnbonus;}}现在,我们就可这样来定义类Manager的对象:Managerboss=newManager(“Bill”,3000,5000);10(3)一个类可以有多个子类,但一个子类却只能有一个父类。如下图所示:(4)final类不能被继承。即,它不允许有子类。因此,它也被称为“最终类”。(5)继承可以添加新的域,也可以修改父类的域,但却不能去除父类的域!EmployeeManagerProgrammerSecretaryExecutive(继承层次图)115.1.5访问权限protected类中的域若带有protected,则其子类将可直接访问它们。然而,JAVA也允许本包的类访问它们。privatenameprivateidprotectednextIdEmployee()getName()getSalary()getId()privatenameprivateidprotectednextIdEmployee()getName()getSalary()getId()privatebonussetBonus()getBonus()类Employee类Manager类Manager可直接访问本包类可直接访问成员及方法的访问权限小结:1.只对本类可见.(private)2.对一切类可见.(public)3.对子类及本包类可见.(protected)4.对本包类可见.(缺省符)125.1.6子类对象的初始化在创建子类对象时,不但要对子类的成员进行初始化,而且还要对父类的成员进行初始化。正如你在前面例5-2中所看到的,在子类的构造方法中,只能通过关键字super去调用父类的构造方法来完成对父类成员的初始化。JAVA规定:super调用必须出现在子类构造方法中的第一行!若你没有给出super调用,那么,系统首先将自动调用父类的默认构造方法(或无参数构造方法)!正如你所知道的那样,每当创建一个子类对象时,系统将会调用子类的构造方法。然而,在JAVA中,子类构造方法的执行过程却是非常有趣的!13你马上就会看到,一个构造方法被调用后,其详细的执行过程:(i)该对象的所有成员被初始化为默认值。(ii)调用父类构造方法。(iii)执行子类的初始化语句。(iv)执行子类构造方法中的代码。下面的演示可以来说明上述过程。classT{intx=1;T(){System.out.println(“x=“+x);}}如,创建类T对象:Tt=newT();则类T构造方法的执行过程为:(i)x=0(ii)x=1(iii)执行代码:输出x=1如,创建类S对象:Ss=newS();则类S构造方法的执行过程为:(i)y=0(i)x=0(ii)x=1(iii)执行T代码:输出x=1(iii)y=2(iv)执行S代码:输出y=2classSextendsT{inty=2;S(){super();System.out.println(“y=“+y);}}145.2成员的隐藏和方法的重构当子类声明了一个与父类中同名的成员后,这个成员就“隐藏”了父类中的同名成员;当子类声明了一个与父类中同名的方法后,这个方法就“重构”(或“覆盖”)了父类中的同名方法。5.2.1成员的隐藏当子类“隐藏”了父类中的同名成员后,子类中将不能用该成员名去访问父类的同名成员,但可用以下两种形式来访问之:(1)super.成员名(2)父类名.成员名classBextendsA{intx=2;voidshow(){System.out.println(“x=“+x);System.out.println(“super.x=“+super.x);System.out.println(“A.x=“+A.x);}}classA{intx=1;}方法show的打印结果:x=2super.x=1A.x=115classCextendsB{intx=100,y=200;voidshow(){System.out.println(“C.x=”+x+”,C.y=”+y);System.out.println(“B.x=”+B.x+”,B.y=”+B.y);System.out.println(“super.x=”+super.x+”,super.y=”+super.y);System.out.println(“A.x=”+A.x+”,A.y=”+A.y);}}下面我们再来看一个例子。你要注意其中super的作用及“超类”的概念。在这里,“A.x”中的“A”称为“超类”,用于特指继承链上的某个祖先类。classBextendsA{intx=10;}classA{intx=1,y=2;}x=1y=2x=10x=100