资源预览内容
第1页 / 共144页
第2页 / 共144页
第3页 / 共144页
第4页 / 共144页
第5页 / 共144页
第6页 / 共144页
第7页 / 共144页
第8页 / 共144页
第9页 / 共144页
第10页 / 共144页
亲,该文档总共144页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述
第5章 消息、继承与多态第5章 消息、继承与多态5.1 消息消息5.2 访问控制访问控制5.3 多态机制多态机制5.4 继承机制继承机制5.5 抽象类、接口与包抽象类、接口与包第5章 消息、继承与多态5.1 消消 息息5.1.1 消息的概念消息的概念在日常生活中,人与人之间要进行交流,某人可以向别人提供服务,例如,他可以开汽车,教学生学习等;同时他也需要别人为他提供服务,例如,他要吃饭但不可能自己去种地,要穿衣不可能自己去织布,他必须请求别人帮助;同样,他什么时间讲课,也必须得到他人的请求或命令。“请求”或“命令”便是人与人进行交流的手段。第5章 消息、继承与多态在面向对象的系统中,把“请求”或“命令”抽象成“消息”,对象之间的联系是通过消息传递来实现的。当系统中的其他对象请求这个对象执行某个服务时,它就响应这个请求,完成指定的服务。通常,我们把发送消息的对象称为发送者,把接收消息的对象称为接收者。对象间的联系,只能通过消息传递来进行。对象也只有在收到消息时,才被激活,去完成消息要求的功能。第5章 消息、继承与多态消息就是向对象发出服务请求,是对数据成员和成员方法的引用。因此,它应该含有下述信息:提供服务的对象标识对象名、服务标识方法名、输入信息实际参数、回答信息返回值或操作结果。消息具有三个性质:(1)同一对象可接收不同形式的多个消息,产生不同的响应。(2)相同形式的消息可以发送给不同对象,所做出的响应可以是截然不同的。(3)消息的发送可以不考虑具体的接收者,对象可以响应消息,也可以对消息不予理会,对消息的响应并不是必须的。第5章 消息、继承与多态5.1.2 公有消息和私有消息公有消息和私有消息在面向对象系统中,消息分为两类:公有消息和私有消息。当有一批消息同属于一个对象时,由外界对象直接发送给这个对象的消息称为公有消息;对象自己发送给本身的消息称为私有消息。私有消息对外是不开放的,外界不必了解它。外界对象只能向此对象发送公有消息,而不能发送私有消息,私有消息是由对象自身发送的。第5章 消息、继承与多态5.1.3 特定于对象的消息特定于对象的消息特定于对象的消息是指将所有能支持此对象可接受消息的方法集中在一起,形成一个大消息,称为特定于对象的消息。这些消息让对象执行这个方法而不管它可能做什么及怎么做。特定于对象的消息可分为三种类型:(1)可以返回对象内部状态的消息。(2)可以改变对象内部状态的消息。(3)可以做一些特定操作,改变系统状态的消息。第5章 消息、继承与多态【示例程序c5_1.java】classstudentpublicStringname;publiccharsex;publicintno;publicintage;student(intcno,Stringcname,charcsex,intcage)name=cname;sex=csex;no=cno;age=cage;第5章 消息、继承与多态publicvoidshowNo()System.out.println(No:+no);publicvoidshowName()System.out.println(Name:+name);publicvoidshowSex()System.out.println(Sex:+sex);publicvoidshowAge()System.out.println(age:+age);classstudentScoreprivateintno;privatedoublescore;publicvoidsendScore(intcno,doublecscore)/下面两句是对象发送给自身的消息,要求给自己的数据成员赋值,/这是一种私有消息,外界是不知道的。no=cno;score=cscore;第5章 消息、继承与多态voidprintScore()System.out.println(No:+no+score:+score);publicclassc5_1publicstaticvoidmain(Stringargs)intm;/下面两句发送new消息给类student,要求创建类student的对象st1,st2studentst1=newstudent(101,zhangli,F,18);studentst2=newstudent(102,hongbing,M,17);/发送new消息给类studentScore,要求创建类studentScore的对象sc1,sc2studentScoresc1=newstudentScore();studentScoresc2=newstudentScore();第5章 消息、继承与多态/*向对象st1发送显示学号、名字、年龄的消息。这些消息都是公有消息,形成了同一对象可接收不同形式的多个消息,产生不同的响应。*/st1.showNo();/显示学号的消息st1.showName();/显示姓名的消息st1.showAge();/显示年龄的消息st1.age=20;/修改对象数据成员的消息,修改st1的年龄。m=st1.age;/返回对象的数据成员的消息,将返回消息赋给变量m。System.out.println(m=+m);/*向对象st2发送2个显示信息的消息,与st1相同,显示学号及名字。这些消息都是公有消息,说明了相同形式的消息可以送给不同对象,所做出的响应可以是截然不同的。*/第5章 消息、继承与多态st2.showNo();st2.showName();/向对象sc1,sc2各发送一个按学号输入成绩单的消息,这些消息都是公有消息sc1.sendScore(101,97);sc2.sendScore(102,84);/向对象sc1,sc2各发送一个打印消息,这些消息都是公有消息sc1.printScore();sc2.printScore();第5章 消息、继承与多态运行结果如下:No:101Name:zhangliage:18m=20No:102Name:hongbingNo:101score:97.0No:102score:84.0第5章 消息、继承与多态5.2 访访 问问 控控 制制一个类总能够访问自己的数据成员和成员方法。但是,其他类是否能访问这个类的数据成员或成员方法,是由该类的访问控制符及该类数据成员和成员方法的访问控制符决定的。这就是说,访问控制符是一组限定类、数据成员或成员方法是否可以被其他类访问的修饰符。类的访问控制符只有public一个,缺省访问控制符时具有“友好访问”的特性。数据成员和成员方法的访问控制符有public、private、protected和缺省访问控制符等几种。见表5.1。第5章 消息、继承与多态表表5.1 类、数据成员和成员方法的访问控制符及其作用类、数据成员和成员方法的访问控制符及其作用类数据成员与方法Public缺省public所有类包中类(含当前类)protected包中类(含当前类),所有子类包中类(含当前类)缺省(friendly)包中类(含当前类)包中类(含当前类)private当前类本身当前类本身第5章 消息、继承与多态5.2.1 公共访问控制符公共访问控制符publicJava的类是通过包的概念来组织的,简单地说,定义在同一个程序文件中的所有类都属于同一个包。处于同一个包中的类都是可见的,即可以不需任何说明而方便地互相访问和引用。而对于不同包中的类,一般说来,它们相互之间是不可见的,当然也不可能互相引用。然而,当一个类被声明为public时,只要在其他包的程序中使用import语句引入这个public类,就可以访问和引用这个类,创建这个类的对象,访问这个类内部可见的数据成员和引用它的可见的方法。例如,Java类库中的许多类都是公共类,我们在程序中就是通过import语句将其引入的。第5章 消息、继承与多态当一个类的访问控制符为public时,表明这个类作为整体对其他类是可见和可使用的,这个类就具有了被其他包中的类访问的可能性。但是,处于不同包中的public类作为整体对其他类是可见的,并不代表该类的所有数据成员和成员方法也同时对其他类是可见的,这得由这些数据成员和成员方法的修饰符来决定。只有当public类的数据成员和成员方法的访问控制符也被声明为public时,这个类的所有用public修饰的数据成员和成员方法也同时对其他类是可见的。在程序设计时,如果希望某个类能作为公共工具供其他的类和程序使用,则应该把类本身和类内的方法都定义成public。例如,Java类库中的标准数学函数类math和标准数学函数方法。需要注意的是,数据成员和成员方法的访问控制符被声明为public时,会造成安全性和封装性下降,所以一般应尽量少用。第5章 消息、继承与多态5.2.2 缺省访问控制符缺省访问控制符如果一个类没有访问控制符,说明它具有缺省的访问控制特性,这种缺省的访问控制特性称为“友好访问”。友好访问规定只有在同一个包中的对象才能访问和引用这些类,因此,又称为包访问性。同样道理,类内的数据成员和成员方法如果没有访问控制符来限定,也具有“友好访问”的特性,它们也具有包访问性,可以被同一个包中的其他类所访问和引用。第5章 消息、继承与多态【示例程序c5_2.java】计算矩形面积。classclassAreapublicdoublelon,wid;/数据成员的修饰符为publicpublicdoublearea(doublex,doubley)/成员方法的修饰符为publicdoubles;/方法内的变量lon=x;wid=y;s=lon*wid;/求矩形面积returns;/返回面积值第5章 消息、继承与多态publicclassc5_2publicstaticvoidmain(Stringargs)doublea=2.2,b=3.1,z;/*在类c5_2中创建被访问类classArea的对象ss,ss可以访问类classArea内的数据成员和成员方法。这就是说,同一包中的类是可见的,可以互相引用*/第5章 消息、继承与多态classAreass=newclassArea();z=ss.area(a,b);/对象ss引用类classArea内的成员方法。System.out.println(z=+z);运行结果:z=6.8200000000000001第5章 消息、继承与多态程序c5_2java中定义了两个类classArea和c5_2。由于它们是同一个程序文件c5_2.java中的类,所以属于同一个包,它们之间是可见的,所以可以互相引用。如果将这个例子中的类classArea的数据成员和成员方法改写为缺省访问控制符,并不影响程序的正确运行和所得结果,原因是它们属于同一个包。第5章 消息、继承与多态classclassAreadoublelon,wid;/缺省数据成员的修饰符doublearea(doublex,doubley)/缺省成员方法的修饰符doubles;/方法内的变量lon=x;wid=y;s=lon*wid;/求矩形面积returns;/返回面积值第5章 消息、继承与多态5.2.3 私有访问控制符私有访问控制符private用private修饰的数据成员或成员方法只能被该类自身所访问和修改,而不能被任何其他类(包括该类的子类)来访问和引用。它提供了最高的保护级别。当其他类希望获取或修改私有成员时,需要借助于类的方法来实现。【示例程序c5_3.java】用private修饰的数据成员。classP1privateintn=9;/私有数据成员nintnn;P1()/构造方法nn=n+;/可以被该类的对象自身访问和修改第5章 消息、继承与多态voidma()System.out.println(n=+n);/可以被该类的对象自身访问publicclassc5_3extendsP1/类classc5_3是类P1的子类publicstaticvoidmain(Stringargs)P1m1=newP1();System.out.println(m1.nn=+m1.nn);/System.out.println(m1.n=+m1.n);错,不能引用父类的私有成员m1.ma();/可以引用P1类自身的成员方法运行结果:m1.nn=9n=10第5章 消息、继承与多态5.2.4 保护访问控制符保护访问控制符protected用protected修饰的成员变量可以被三种类引用:该类自身、与它在同一个包中的其他类、在其他包中的该类的子类。使用protected修饰符的主要作用是允许其他包中的它的子类来访问父类的特定属性。【示例程序c5_4.java】classclassAreaprivatedoublelon,wid;/private修饰的私有数据成员protecteddoublearea(doublex,doubley)/protected修饰的成员方法第5章 消息、继承与多态doubles;lon=x;wid=y;s=lon*wid;returns;publicclassc5_4/类c5_4与类classArea在一个包中publicstaticvoidmain(Stringargs)doublea=2.2,b=3.1,z;第5章 消息、继承与多态/*在类c5_4中创建被访问类classArea的对象ss,ss可以访问类classArea内的成员方法,因为该方法是protected修饰的。但不能访问该类的数据成员,因为它们是用private修饰的私有数据成员。*/classAreass=newclassArea();z=ss.area(a,b);System.out.println(z=+z);运行结果:z=68.200000000000001第5章 消息、继承与多态5.3 多多 态态 机机 制制多态是面向对象系统中的又一重要特性,它描述的是同名方法可以根据发送消息的对象传送参数的不同,采取不同的行为方式的特性。面向对象系统中采用多态,大大提高了程序的抽象程度和简洁性,更重要的是,它最大限度地降低了类和程序模块之间的耦合性,提高了类模块的封闭性,使得它们不需了解对方的具体细节,就可以很好地共同工作。这一点对程序的设计、开发和维护都有很大的好处。第5章 消息、继承与多态5.3.1 多态的概念多态的概念多态是指一个程序中同名的不同方法共存的情况。这些方法同名的原因是它们的最终功能和目的都相同,但是由于在完成同一功能时,可能遇到不同的具体情况,所以需要定义含不同的具体内容的方法,来代表多种具体实现形式。它是面向对象程序设计中的一个特性,其目的是为了提高程序的抽象度、封闭性和简洁性,统一一个或多个相关类对外的接口。Java中提供两种多态机制:重载与覆盖。第5章 消息、继承与多态5.3.2 方法重载方法重载在同一类中定义了多个同名而不同内容的成员方法时,我们称这些方法是重载(override)的方法。重载的方法主要通过形式参数列表中参数的个数、参数的数据类型和参数的顺序等方面的不同来区分。在编译期间,Java编译器检查每个方法所用的参数数目和类型,然后调用正确的方法。第5章 消息、继承与多态【示例程序c5_5.java】加法重载的例子。importjava.awt.*;importjava.applet.*;publicclassc5_5extendsAppletintadd(inta,intb)/重载的方法1return(a+b);doubleadd(doublex,doubley)/重载的方法2return(x+y);doubleadd(doublex,doubley,doublez)/重载的方法3return(x+y+z);publicvoidpaint(Graphicsg)g.drawString(Sumis:+add(8.5,2.3),5,10);g.drawString(Sumis:+add(21,38),5,30);g.drawString(Sumis:+add(8.5,2.3,8.5+2.3),5,50);第5章 消息、继承与多态运行结果如下:Sumis:10.8Sumis:59Sumis:21.6该类中定义了三个名为add的方法:第一个方法是计算两个整数的和;第二个方法是计算两个浮点数的和;第三个方法是计算三浮点数据的和。编译器根据方法引用时提供的实际参数选择执行对应的重载方法。第5章 消息、继承与多态5.3.3 覆盖覆盖由于面向对象系统中的继承机制,子类可以继承父类的方法。但是,子类的某些特征可能与从父类中继承来的特征有所不同,为了体现子类的这种个性,Java允许子类对父类的同名方法重新进行定义,即在子类中定义与父类中已定义的相同名而内容不同的方法。这种多态被称为覆盖(overload)。由于覆盖的同名方法是存在于子类对父类的关系中,所以只需在方法引用时指明引用的是父类的方法还是子类的方法,就可以很容易地把它们区分开来。具体应用请参阅第5章 消息、继承与多态5.4 继继 承承 机机 制制5.4.1 继承的概念继承的概念同类事物具有共同性,在同类事物中,每个事物又具有其特殊性。运用抽象的原则舍弃对象的特殊性,抽取其共同性,则得到一个适应于一批对象的类,这便是一般类,而把具有特殊性的类称为特殊类。也就是说,如果类B具有类A的全部属性和方法,而且又具有自己特有的某些属性和方法,则把类A称作一般类,把类B叫做类A的特殊类。例如:考虑轮船和客轮这两个类。轮船具有吨位、时速、吃水线等属性,并具有行驶、停泊等服务;客轮具有轮船的全部属性与服务,又有自己的特殊属性(如载客量)和服务(如供餐等)。若把轮船看做一般类,则客轮是轮船的特殊类。第5章 消息、继承与多态在面向对象程序设计中运用继承原则,就是在每个由一般类和特殊类形成的一般特殊结构中,把一般类的对象实例和所有特殊类的对象实例都共同具有的属性和操作一次性地在一般类中进行显式的定义,在特殊类中不再重复地定义一般类中已经定义的东西,但是在语义上,特殊类却自动地、隐含地拥有它的一般类(以及所有更上层的一般类)中定义的属性和操作。特殊类的对象拥有其一般类的全部或部分属性与方法,称作特殊类对一般类的继承。第5章 消息、继承与多态继承所表达的就是一种对象类之间的相交关系,它使得某类对象可以继承另外一类对象的数据成员和成员方法。若类B继承类A时,则属于B的对象便具有类A的全部或部分性质(数据属性)和功能(操作)。我们称被继承的类A为基类、父类或超类,而称继承类B为A的派生类或子类。父类与子类的层次关系如图5.1所示。第5章 消息、继承与多态图5.1父类与子类的层次关系运输工具轮船汽车飞机载重汽车公共汽车货轮客轮空中加油机客机上一类的子类下一类的父类子类第5章 消息、继承与多态继承避免了对一般类和特殊类之间共同特征进行的重复描述。同时,通过继承可以清晰地表达每一项共同特征所适应的概念范围在一般类中定义的属性和操作适应于这个类本身以及它以下的每一层特殊类的全部对象。运用继承原则使得系统模型比较简练也比较清晰。第5章 消息、继承与多态5.4.2 继承的特征继承的特征一般来说,继承具有下述特征:(1)继承关系是传递的。若类C继承类B,类B继承类A时,则类C既有从类B继承下来的属性与方法,也有从类A中继承下来的属性与方法,还可以有自己新定义的属性和方法。继承来的属性和方法尽管是隐式的,却仍是类C的属性和方法。继承是在一些比较一般的类的基础上构造、建立和扩充新类的最有效的手段。第5章 消息、继承与多态(2)继承简化了人们对事物的认识和描述,能清晰体现相关类间的层次结构关系。(3)提供软件复用功能。若类B继承类A,建立类B时只需要再描述与基类(类A)不同的少量特征(数据成员和成员方法)。这种做法能减小代码和数据的冗余度,大大增加程序的重用性。(4)通过增强一致性来减少模块间的接口和界面,大大增加程序的易维护性。第5章 消息、继承与多态(5)提供多重继承机制。从理论上说,一个类可以是多个一般类的特殊类,它可以从多个一般类中继承属性与方法,这便是多重继承。而Java出于安全性和可靠性的考虑,仅支持单重继承,而通过使用接口机制来实现多重继承。如图5.2为一个单重继承与多重继承的例子。在这个模型中,“本科生”、“研究生”、“脱产研究生”都为单继承,而“在职研究生”为多重继承,因为它不仅继承“学生”/“研究生”的属性和行为,还继承“教师”的属性和行为。第5章 消息、继承与多态图5.2单重继承与多重继承学生本科生教师研究生脱产研究生在职研究生第5章 消息、继承与多态5.4.3 Java用用extends指明继承关系指明继承关系在Java程序设计中,继承是通过extends关键字来实现的。在定义类时使用extends关键字指明新定义类的父类,新定义的类称为指定父类的子类,这样就在两个类之间建立了继承关系。这个新定义的子类可以从父类那里继承所有非private的属性和方法作为自己的成员。实际上,在定义一个类而不给出extends关键字及父类名时,默认这个类是系统类object的子类。下面分不同情况来讲解。第5章 消息、继承与多态1数据成员的继承数据成员的继承子类可以继承父类的所有非私有的数据成员。【示例程序c5_6.java】classa1intx=25;privateintz;/不能被子类继承的私有数据成员zclassc5_6extendsa1/a1是c5_6的父类,c5_6是a1的子类publicstaticvoidmain(StringargS)c5_6p=newc5_6();System.out.println(p.x=+p.x);/输出继承来的数据成员的值/System.out.println(p.z=+p.z);错,不能继承private修饰的z运行结果:p.x=25第5章 消息、继承与多态2数据成员的隐藏数据成员的隐藏数据成员的隐藏是指在子类中重新定义一个与父类中已定义的数据成员名完全相同的数据成员,即子类拥有了两个相同名字的数据成员,一个是继承父类的,另一个是自己定义的。当子类引用这个同名的数据成员时,默认操作是它自己定义的数据成员,而把从父类那里继承来的数据成员“隐藏”起来。当子类要引用继承自父类的同名数据成员时,可使用关键字super引导,这部分内容在5.4.4节介绍。第5章 消息、继承与多态【示例程序c5_7.java】classa1intx=8;/父类中定义了数据成员xclassc5_7extendsa1intx=24;/子类中也定义了数据成员xpublicstaticvoidmain(StringargS)ints1,s2;a1p=newa1();/创建父类的对象pc5_7p1=newc5_7();/创建子类的对象p1s1=p.x;s2=p1.x;/子类对象引用自己的数据成员,把父类数据成员“隐藏”起来System.out.println(s1=+s1);System.out.println(s2=+s2);运行结果:s1=8s2=24第5章 消息、继承与多态3成员方法的继承成员方法的继承子类可以继承父类的非私有成员方法。下面的程序说明这一问题。【示例程序c5_8.java】classa1intx=0,y=1;voidMyp()System.out.println(x=+x+y=+y);privatevoidPrintme()System.out.println(x=+x+y=+y);第5章 消息、继承与多态publicclassc5_8extendsa1publicstaticvoidmain(Stringarg)intz=3;c5_8p1=newc5_8();p1.Myp();/p1.Printme();错,不能继承父类的private方法运行结果:x=0y=1第5章 消息、继承与多态 4成员方法的覆盖成员方法的覆盖(overload)子类可以重新定义与父类同名的成员方法,实现对父类方法的覆盖。方法的覆盖与数据成员的隐藏的不同之处在于:子类隐藏父类的数据成员只是使之不可见,父类同名的数据成员在子类对象中仍然占有自己的独立的内存空间;子类方法对父类同名方法的覆盖将清除父类方法占用的内存,从而使父类方法在子类对象中不复存在。第5章 消息、继承与多态【示例程序c5_9.java】classa1intx=10;inty=31;publicvoidPrintme()System.out.println(x=+x+y=+y);publicclassc5_9extendsa1intz=35;publicvoidPrintme()/子类中定义了与父类同名的成员方法,实现覆盖System.out.println(z=+z);publicstaticvoidmain(Stringarg)a1p2=newa1();/创建父类对象p2c5_9p1=newc5_9();/创建子类对象p1p1.Printme();/子类对象引用子类方法,覆盖了父类的同名方法p2.Printme();/父类对象引用父类方法第5章 消息、继承与多态运行结果是:z=35x=10y=31方法的覆盖中需要注意的是:子类在重新定义父类已有的方法时,应保持与父类完全相同的方法名、返回值类型和参数列表,否则就不是方法的覆盖,而是子类定义自己特有的方法,与父类的方法无关。第5章 消息、继承与多态5.4.4 this与与super 1this的使用场合的使用场合在一些容易混淆的场合,例如,当成员方法的形参名与数据成员名相同,或者成员方法的局部变量名与数据成员名相同时,在方法内借助this来明确表示引用的是类的数据成员,而不是形参或局部变量,从而提高程序的可读性。简单地说,this代表了当前对象的一个引用,可将其理解为对象的另一个名字,通过这个名字可以顺利地访问对象、修改对象的数据成员、调用对象的方法。归纳起来,this的使用场合有下述三种:第5章 消息、继承与多态(1)用来访问当前对象的数据成员,其使用形式如下:this.数据成员(2)用来访问当前对象的成员方法,其使用形式如下:this.成员方法(参数)(3)当有重载的构造方法时,用来引用同类的其他构造方法,其使用形式如下:this(参数)第5章 消息、继承与多态【示例程序c5_10.java】classa1intx=0;inty=1;publicvoidPrintme()System.out.println(x=+x+y=+y);System.out.println(Iaman+this.getClass().getName();/用this来访问当前对象的成员方法,通过this表示当前对象来打印当前对象的类名。其中/getClass()和getName()是系统类库中提供的方法。第5章 消息、继承与多态publicclassc5_10extendsa1publicstaticvoidmain(Stringarg)c5_10p1=newc5_10();p1.Printme();运行结果如下:x=0y=1Iamanc5_10第5章 消息、继承与多态【示例程序c5_11.java】classclassAreadoublex,y;doublearea(doublex,doubley)doubles;this.x=x;/借助this来表示引用的是类数据成员y=y;/赋值号左边的y是数据成员,右边的y是参数,请复习4.1.9节s=this.x*y;/不能写成this.y,因为上一句没有这样写returns;第5章 消息、继承与多态publicclassc5_11extendsclassAreapublicstaticvoidmain(Stringargs)doublea=2.2,b=3.1,z;c5_11ss=newc5_11();/创建一个ss对象z=ss.area(a,b);/引用父类对象的成员方法求面积System.out.println(z=+z);运行结果是:z=6.820000000000001第5章 消息、继承与多态【示例程序c5_12.java】计算圆的面积和周长。publicclassc5_12publicstaticvoidmain(Stringargs)doublex;circlecir=newcircle(5.0);x=cir.area();System.out.println(圆的面积=+x);x=cir.perimeter();System.out.println(圆的周长=+x);第5章 消息、继承与多态classcircledoubler;/定义半径finaldoublePI=3.14159265359;/定义圆周率publiccircle(doubler)/类的构造方法this.r=r;doublearea()/计算圆面积的方法returnPI*r*r;/通过构造方法给r赋值doubleperimeter()/计算圆周长的方法return2*(this.area()/r);/使用this变量获取圆的面积第5章 消息、继承与多态运行结果:圆的面积=78.53981633974999圆的周长=31.415926535899995第5章 消息、继承与多态 2super的使用场合的使用场合super表示的是当前对象的直接父类对象,是当前对象的直接父类对象的引用。所谓直接父类是相对于当前对象的其他“祖先”类而言。例如,假设类A派生出子类B,类B又派生出自己的子类C,则B是C的直接父类,而A是C的祖先类。super代表的就是直接父类。若子类的数据成员或成员方法名与父类的数据成员或成员方法名相同时,当要调用父类的同名方法或使用父类的同名数据成员,则可用关键字super来指明父类的数据成员和方法。super的使用方法有三种:第5章 消息、继承与多态(1)用来访问直接父类隐藏的数据成员,其使用形式如下:super.数据成员(2)用来调用直接父类中被覆盖的成员方法,其使用形式如下:super.成员方法(参数)(3)用来调用直接父类的构造方法,其使用形式如下:super(参数)第5章 消息、继承与多态【示例程序c5_13.java】classa1intx=4;inty=1;publicvoidPrintme()System.out.println(x=+x+y=+y);System.out.println(classname:+this.getClass().getName();publicclassc5_13extendsa1第5章 消息、继承与多态intx;publicvoidPrintme()intz=super.x+6;/引用父类(即a1类)的数据成员super.Printme();/调用父类(即a1类)的成员方法System.out.println(Iaman+this.getClass().getName();x=5;System.out.println(z=+z+x=+x);/打印子类的数据成员第5章 消息、继承与多态publicstaticvoidmain(Stringarg)intk;a1p1=newa1();c5_13p2=newc5_13();p1.Printme();p2.Printme();/super.Printme();/错,在static方法中不能引用非static成员方法/k=super.x+23;/错,在static方法中不能引用非static数据成员第5章 消息、继承与多态运行结果如下:x=4y=1classname:a1x=4y=1classname:c5_13Iamanc5_13z=10x=5第5章 消息、继承与多态5.4.5 构造方法的重载与继承构造方法的重载与继承 1构造方法的重载构造方法的重载一个类的若干个构造方法之间可以相互调用。当一个构造方法需要调用另一个构造方法时,可以使用关键字this,同时这个调用语句应该是整个构造方法的第一个可执行语句。使用关键字this来调用同类的其他构造函数时,优点同样是可以最大限度地提高对已有代码的利用程度,提高程序的抽象度和封装性,减少程序的维护工作量。第5章 消息、继承与多态【示例程序c5_14.java】classaddclasspublicintx=0,y=0,z=0;/以下是多个同名不同参数的构造方法addclass(intx)/可重载的构造方法1this.x=x;addclass(intx,inty)/可重载的构造方法2this(x);/当前构造方法调用可重载的构造方法1this.y=y;第5章 消息、继承与多态addclass(intx,inty,intz)/可重载的构造方法3this(x,y);/当前构造方法调用可重载的构造方法2this.z=z;publicintadd()returnx+y+z;publicclassc5_14第5章 消息、继承与多态publicstaticvoidmain(Stringargs)addclassp1=newaddclass(2,3,5);addclassp2=newaddclass(10,20);addclassp3=newaddclass(1);System.out.println(x+y+z=+p1.add();System.out.println(x+y=+p2.add();System.out.println(x=+p3.add();第5章 消息、继承与多态运行结果:x+y+z=10x+y=30x=1第5章 消息、继承与多态2构造方法的继承构造方法的继承子类可以继承父类的构造方法,构造方法的继承遵循以下的原则:(1)子类无条件地继承父类的不含参数的构造方法。(2)如果子类自己没有构造方法,则它将继承父类的无参数构造方法作为自己的构造方法;如果子类自己定义了构造方法,则在创建新对象时,它将先执行继承自父类的无参数构造方法,然后再执行自己的构造方法。(3)对于父类的含参数构造方法,子类可以通过在自己的构造方法中使用super关键字来调用它,但这个调用语句必须是子类构造方法的第一个可执行语句。第5章 消息、继承与多态【示例程序c5_15.java】classaddclasspublicintx=0,y=0,z=0;addclass(intx)/父类可重载的构造方法1this.x=x;addclass(intx,inty)/父类可重载的构造方法2this.x=x;this.y=y;addclass(intx,inty,intz)/父类可重载的构造方法3this.x=x;this.y=y;this.z=z;publicintadd()returnx+y+z;第5章 消息、继承与多态publicclassc5_15extendsaddclassinta=0,b=0,c=0;c5_15(intx)/子类可重载的构造方法1super(x);a=x+7;c5_15(intx,inty)/子类可重载的构造方法2super(x,y);a=x+5;b=y+5;c5_15(intx,inty,intz)/子类可重载的构造方法3super(x,y,z);a=x+4;b=y+4;c=z+4;第5章 消息、继承与多态publicintadd()System.out.println(super:x+y+z=+super.add();returna+b+c;publicstaticvoidmain(Stringargs)c5_15p1=newc5_15(2,3,5);c5_15p2=newc5_15(10,20);c5_15p3=newc5_15(1);System.out.println(a+b+c=+p1.add();System.out.println(a+b=+p2.add();System.out.println(a=+p3.add();第5章 消息、继承与多态运行结果如下:super:x+y+z=10a+b+c=22super:x+y+z=30a+b=40super:x+y+z=1a=8第5章 消息、继承与多态5.4.6 向方法传递对象向方法传递对象前面已讲过传递给方法的参数可以是表达式(如常量、变量)、对象等,并说明传递给方法的参数若是变量,则只能由实参传递给形参,而不能由形参带回,它是一种单向值传递。也就是说,在方法的引用过程中,对于形参变量值的修改并不影响实参变量的值。但是,传递给方法的参数若是对象,则方法可以对其做永久性修改。第5章 消息、继承与多态【示例程序c5_16.java】importjava.awt.*;importjava.applet.*;classstudentpublicStringName;publicintage=16;publicintscore=0;publicvoidShowStudent(Graphicsg,intx,inty)g.drawString(Name:+Name,x,y);g.drawString(age:+age,x,y+20);g.drawString(score:+score,x,y+40);第5章 消息、继承与多态publicclassc5_16extendsAppletpublicvoidstudentAttributes(students,StringName,intage,intscore)s.Name=Name;s.age=age;s.score=score;第5章 消息、继承与多态publicvoidpaint(Graphicsg)studentst1=newstudent();/创建对象st1studentst2=newstudent();/创建对象st2studentAttributes(st1,zhang,23,81);/对象st1作为实参studentAttributes(st2,li,24,90);/对象st2作为实参st1.ShowStudent(g,25,25);/执行此方法可发现对象st1将新值带回st2.ShowStudent(g,25,120);/再次执行此方法可发现对象st2将新值带回运行结果如图5.3所示。第5章 消息、继承与多态图5.3程序c5_16的运行结果第5章 消息、继承与多态5.4.7 类转换类转换类转换就是指父类对象与子类对象之间在一定条件下的相互转换。父类对象与子类对象之间的相互转换规则如下:(1)父类对象与子类对象之间可以隐式转换(也称默认转换),也可以显式转换(也称强制转换)。(2)处于相同类层次的类的对象不能进行转换。(3)子类对象可以转换成父类对象,但对数据成员的引用必须使用强制转换。第5章 消息、继承与多态【示例程序c5_17.java】classSuperClass/定义父类intx=1,y=2,t=98;voidshow()System.out.println(x+y=+(x+y);classSubClassextendsSuperClass/定义子类inty=9,z=7;voidshow()System.out.println(y*z=+(y*z);第5章 消息、继承与多态publicclassc5_17/使用父类与子类publicstaticvoidmain(Stringargs)SuperClasssc,scf;/声明父类对象SubClasssb,sbf;/声明子类对象sc=newSuperClass();sb=newSubClass();System.out.println(sc.x=+sc.x+sc.y=+sc.y+sc.t=+sc.t);/System.out.println(sc.z=+sc.z);错,父类对象不能引用子类数据成员zsc.show();第5章 消息、继承与多态/子类对象可以被视为是其父类的一个对象,因此可以引用父类的数据成员System.out.println(sb.y=+sb.y+sb.z=+sb.z+sb.t=+sb.t);sb.show();scf=(SuperClass)sb;/强制转换,子类引用转换为父类引用/也可以用默认转换,写成:scf=sb;/下一条语句中,scf引用的数据成员y是父类的y,scf不可引用zSystem.out.println(scf.x=+scf.x+scf.y=+scf.y);System.out.print(scf.show():t);/引用子类的成员方法scf.show();sbf=(SubClass)scf;/强制转换,父类引用转换为子类引用System.out.println(sbf.x=+sbf.x+sbf.y=+sbf.y+sbf.z=+sbf.z);System.out.print(sbf.show():t);sbf.show();第5章 消息、继承与多态运行结果:sc.x=1sc.y=2sc.t=98x+y=3sb.y=9sb.z=7sb.t=98y*z=63scf.x=1scf.y=2scf.show():y*z=63sbf.x=1sbf.y=9sbf.z=7sbf.show():y*z=63第5章 消息、继承与多态5.4.8 继承与封装的关系继承与封装的关系在面向对象系统中,有了封装机制以后,对象之间只能通过消息传递进行通信。那么,继承机制的引入是否削弱了对象概念的封装性?继承和封装是否矛盾?其实这两个概念并没有实质性的冲突,在面向对象系统中,封装性主要指的是对象的封装性,即将属于某一类的一个具体的对象封装起来,使其数据和操作成为一个整体。在引入了继承机制的面向对象系统中,对象依然是封装得很好的实体,其他对象与它进行通讯的途径仍然只有一条,那就是发送消息。类机制是一种静态机制,不管是基类还是派生类,对于对象来说,它仍然是一个类的实例,既可能是基类的实例,也可能是派生类的实例。因此继承机制的引入丝毫没有影响对象的封装性。第5章 消息、继承与多态从另一角度看,继承和封装机制还具有一定的相似性,它们都是一种共享代码的手段。继承是一种静态共享代码的手段,通过派生类对象的创建,可以接受某一消息,启动其基类所定义的代码段,从而使基类和派生类共享了这一段代码。封装机制所提供的是一种动态共享代码的手段,通过封装,我们可将一段代码定义在一个类中,在另一个类所定义的操作中,我们可以通过创建该类的实例,并向它发送消息而启动这一段代码,同样也达到共享的目的。第5章 消息、继承与多态5.5 抽象类、接口与包抽象类、接口与包5.5.1 抽象类抽象类假设我们要编写一个计算矩形、三角形和圆的面积与周长的程序,若按前面所学的方式编程,我们必须定义四个类:圆类、三角形类、矩形类和使用前三个类的公共类,它们之间没有继承关系,如图5.4所示。程序写好后虽然能执行,但从程序的整体结构上看,三个类之间的许多共同属性和操作在程序中没有很好地被利用,致使重复编写代码,降低了程序的开发效率,且使出现错误的机会增加。第5章 消息、继承与多态图5.4具有相同特征却彼此独立的几个类圆类圆心座标半径计算面积计算周长三角形类底边长高计算面积计算周长矩形类长宽计算面积计算周长第5章 消息、继承与多态仔细分析上面例子中的三个类,可以看到这三个类都要计算面积与周长,虽然公式不同但目标相同。因此,我们可以为这三个类抽象出一个父类,在父类里定义圆、三角形和矩形三个类共同的数据成员及成员方法。把计算面积与周长的成员方法名放在父类给予说明,而具体的计算公式再在子类中实现。如图5.5所示。这样,我们通过父类就大概知道子类所要完成的任务,而且,这些方法还可以应用于求解平行四边形、梯形等图形的周长与面积。这种结构就是抽象类的概念。第5章 消息、继承与多态图5.5抽象类及其应用圆类圆心座标半径三角形类底边长高计算面积计算周长长宽抽象类梯形类计算面积计算周长抽象方法计算面积计算周长矩形类计算面积计算周长上、下底边长高第5章 消息、继承与多态在Java程序中用抽象类(abstractclass)来实现自然界的抽象概念。抽象类的作用在于将许多有关的类组织在一起,提供一个公共的类,即抽象类,而那些被它组织在一起的具体的类做为它的子类由它派生出来。抽象类刻画了公有行为的特征,并通过继承机制传送给它的派生类。在抽象类中定义的方法称为抽象方法,这些方法只有方法头的声明,而用一个分号来代替方法体的定义,即只定义成员方法的接口形式,而没有具体操作。只有派生类对抽象成员方法的重定义才真正实现与该派生类相关的操作。在各子类继承了父类的抽象方法之后,再分别用不同的语句和方法体来重新定义它,形成若干个名字相同,返回值相同,参数列表也相同,目的一致但是具体实现有一定差别的方法。抽象类中定义抽象方法的目的是实现一个接口,即所有的子类对外都呈现一个相同名字的方法。第5章 消息、继承与多态抽象类是它的所有子类的公共属性的集合,是包含一个或多个抽象方法的类。使用抽象类的一大优点就是可以充分利用这些公共属性来提高开发和维护程序的效率。对于抽象类与抽象方法的限制如下:(1)凡是用abstract修饰符修饰的类被称为抽象类。凡是用abstract修饰符修饰的成员方法被称为抽象方法。(2)抽象类中可以有零个或多个抽象方法,也可以包含非抽象的方法。第5章 消息、继承与多态(3)抽象类中可以没有抽象方法,但是,有抽象方法的类必须是抽象类。(4)对于抽象方法来说,在抽象类中只指定其方法名及其类型,而不书写其实现代码。(5)抽象类可以派生子类,在抽象类派生的子类中必须实现抽象类中定义的所有抽象方法。(6)抽象类不能创建对象,创建对象的工作由抽象类派生的子类来实现。第5章 消息、继承与多态(7)如果父类中已有同名的abstract方法,则子类中就不能再有同名的抽象方法。(8)abstract不能与final并列修饰同一个类。(9)abstract不能与private,static,final或native并列修饰同一个方法。(10)abstract类中不能有private的数据成员或成员方法。第5章 消息、继承与多态【示例程序c5_18.java】抽象类应用。importjava.awt.*;importjava.applet.*;abstractclassShapes/定义一个抽象类Shapespublicintx,y;/x、y为画图的坐标publicintwidth,height;publicShapes(intx,inty,intwidth,intheight)this.x=x;this.y=y;this.width=width;this.height=height;第5章 消息、继承与多态abstractdoublegetArea();/求图形面积的抽象方法abstractdoublegetPerimeter();/求图形周长的抽象方法classSquareextendsShapes/由抽象类Shapes派生的子类矩形类publicdoublegetArea()return(width*height);publicdoublegetPerimeter()return(2*width+2*height);publicSquare(intx,inty,intwidth,intheight)super(x,y,width,height);classTriangleextendsShapes/由抽象类Shapes派生的子类三角形类第5章 消息、继承与多态publicdoublec;/斜边publicdoublegetArea()return(0.5*width*height);publicdoublegetPerimeter()return(width+height+c);publicTriangle(intx,inty,intbase,intheight)super(x,y,base,height);c=Math.sqrt(width*width+height*height);classCircleextendsShapes/由抽象类Shapes派生的子类圆类第5章 消息、继承与多态publicdoubler;/半径publicdoublegetArea()return(r*r*Math.PI);publicdoublegetPerimeter()return(2*Math.PI*r);publicCircle(intx,inty,intwidth,intheight)super(x,y,width,height);r=(double)width/2.0;publicclassc5_18extendsApplet第5章 消息、继承与多态SquareBox=newSquare(5,15,25,25);Triangletri=newTriangle(5,50,8,4);CircleOval=newCircle(5,90,25,25);publicvoidpaint(Graphicsg)/画正方形g.drawRect(Box.x,Box.y,Box.width,Box.height);g.drawString(BoxArea:+Box.getArea(),50,35);g.drawString(BoxPerimeter:+Box.getPerimeter(),50,55);g.drawString(triArea:+tri.getArea(),50,75);第5章 消息、继承与多态g.drawString(triPerimeter:+tri.getPerimeter(),50,95);g.drawOval(Oval.x,Oval.y,Oval.width,Oval.height);/画圆g.drawString(ovalArea:+Oval.getArea(),50,115);运行结果如图5.6所示。从本例可以看出,类Square、类Circle及类Triangle都由抽象类Shape派生而来,都实现了getArea和getPerimeter抽象方法。第5章 消息、继承与多态图5.6程序c5_18的运行结果第5章 消息、继承与多态5.5.2 接口接口多重继承是指一个子类可以有多个直接父类,该子类可以全部或部分继承所有直接父类的数据成员及成员方法。例如,冷藏车既是一种汽车,也是一种制冷设备,所以它是汽车的子类也是制冷设备的子类。自然界中这种多继承结构到处可见。在面向对象的程序设计语言中,有些语言(如C+)提供了多继承机制。而Java出于安全性、简化程序结构的考虑,不支持类间的多继承而只支持单继承。然而在解决实际问题的过程中,在很多情况下仅仅依靠单继承不能将复杂的问题描述清楚。为了使Java程序的类间层次结构更加合理,更符合实际问题的本质,Java语言提供接口来实现多重继承机制。第5章 消息、继承与多态1声明接口声明接口声明接口的格式如下:修饰符interface接口名extends父接口名列表常量数据成员声明抽象方法声明第5章 消息、继承与多态说明:(1)interface是声明接口的关键字,可以把它看成一个特殊类。(2)接口名要求符合Java标识符规定。(3)修饰符有两种:public和默认。public修饰的接口是公共接口,可以被所有的类和接口使用;默认修饰符的接口只能被同一个包中的其他类和接口使用。第5章 消息、继承与多态(4)父接口列表:接口也具有继承性。定义一个接口时可以通过extends关键字声明该接口是某个已经存在的父接口的派生接口,它将继承父接口的所有属性和方法。与类的继承不同的是一个接口可以有一个以上的父接口,它们之间用逗号分隔。(5)常量数据成员声明:常量数据成员前可以有也可没有修饰符。修饰符是publicfinalstatic和fina1static;接口中的数据成员都是用final修饰的常量,写法如下:修饰符数据成员类型数据成员名=常量值或数据成员名=常量值第5章 消息、继承与多态例如:publicfinalstaticdoublePI=3.14159;finalstaticinta=9;intSUM=100;(等价于finalstaticintSUM=100;)第5章 消息、继承与多态(6)抽象方法声明:接口中的方法都是用abstract修饰的抽象方法。在接口中只能给出这些抽象方法的方法名、返回值和参数列表,而不能定义方法体,即这些接口仅仅是规定了一组信息交换、传输和处理的“接口”。格式如下:返回值类型方法名(参数列表);其中:接口中的方法默认为publicabstract方法。接口中方法的方法体可以由Java语言书写,也可以由其他语言书写。方法体由其他语言书写时,接口方法由native修饰符修饰。第5章 消息、继承与多态从上面的格式可以看出,定义接口与定义类非常相似。实际上完全可以把接口理解成为一种特殊的类,由常量和抽象方法组成的特殊类。一个类只能有一个父类,但是它可以同时实现若干个接口。这种情况下,如果把接口理解成特殊的类,那么这个类利用接口实际上就获得了多个父类,即实现了多重继承。接口定义仅仅是实现某一特定功能的一组功能的对外接口和规范,而不能真正地实现这个功能,这个功能的真正实现是在“继承”这个接口的各个类中完成的,即要由这些类来具体定义接口中各抽象方法的方法体。因而在Java中,通常把对接口功能的“继承”称为“实现”。第5章 消息、继承与多态2定义接口注意事项定义接口注意事项定义接口要注意以下几点:(1)接口定义用关键字interface,而不是用class。(2)接口中定义的数据成员全是finalstatic,即常量。(3)接口中没有自身的构造方法,所有成员方法都是抽象方法。(4)接口也具有继承性,可以通过extends关键字声明该接口的父接口。第5章 消息、继承与多态3. 类实现接口的注意事项类实现接口的注意事项一个类要实现接口,即一个类要调用多个接口时,要注意以下几点:(1)在类中,用implements关键字就可以调用接口。一个类若要调用多个接口时,可在implements后用逗号隔开多个接口的名字。(2)如果实现某接口的类不是abstract的抽象类,则在类的定义部分必须实现指定接口的所有抽象方法,即为所有抽象方法定义方法体,而且方法头部分应该与接口中的定义完全一致,即有完全相同的返回值和参数列表。第5章 消息、继承与多态(3)如果实现某接口的类是abstract的抽象类,则它可以不实现该接口所有的方法。但是对于这个抽象类的任何一个非抽象的子类而言,它们的父类所实现的接口中的所有抽象方法都必须有实在的方法体。这些方法体可以来自抽象的父类,也可以来自子类自身,但是不允许存在未被实现的接口方法。这主要体现了非抽象类中不能存在抽象方法的原则。(4)接口的抽象方法的访问限制符都己指定为public,所以类在实现方法时,必须显式地使用public修饰符,否则将被系统警告为缩小了接口中定义的方法的访问控制范围。第5章 消息、继承与多态【示例程序c5_19.java】将例c5_18.java改写为接口程序。importjava.awt.*;importjava.applet.*;interfaceShapes/定义一个接口abstractdoublegetArea();abstractdoublegetPerimeter();classSquareimplementsShapes/类要实现接口第5章 消息、继承与多态publicintx,y;publicintwidth,height;publicdoublegetArea()return(width*height);publicdoublegetPerimeter()return(2*width+2*height);publicSquare(intx,inty,intwidth,intheight)this.x=x;this.y=y;this.width=width;this.height=height;第5章 消息、继承与多态classTriangleimplementsShapes/类要实现接口publicintx,y;publicintwidth,height;publicdoublec;publicdoublegetArea()return(0.5*width*height);publicdoublegetPerimeter()return(width+height+c);publicTriangle(intx,inty,intbase,intheight)第5章 消息、继承与多态this.x=x;this.y=y;width=base;this.height=height;c=Math.sqrt(width*width+height*height);classCircleimplementsShapes/类要实现接口第5章 消息、继承与多态publicintx,y;publicintwidth,height;publicdoubler;publicdoublegetArea()return(r*r*Math.PI);publicdoublegetPerimeter()return(2*Math.PI*r);publicCircle(intx,inty,intwidth,intheight)this.x=x;this.y=y;this.width=width;this.height=height;r=(double)width/2.0;第5章 消息、继承与多态publicclassc5_19extendsAppletSquareBox=newSquare(5,15,25,25);Triangletri=newTriangle(5,50,8,4);CircleOval=newCircle(5,90,25,25);publicvoidpaint(Graphicsg)g.drawRect(Box.x,Box.y,Box.width,Box.height);g.drawString(BoxArea:+Box.getArea(),50,35);g.drawString(BoxPerimeter:+Box.getPerimeter(),50,55);g.drawString(triArea:+tri.getArea(),50,75);g.drawString(triPerimeter:+tri.getPerimeter(),50,95);g.drawOval(Oval.x,Oval.y,Oval.width,Oval.height);g.drawString(ovalArea:+Oval.getArea(),50,115);第5章 消息、继承与多态图5.7程序c5_19的运行结果第5章 消息、继承与多态【示例程序c5_20.java】将例c5_19.java改写为既有继承类又有接口的程序。importjava.awt.*;importjava.applet.*;interfaceShapesabstractdoublegetArea();abstractdoublegetPerimeter();classCoordinatesintx,y;publicCoordinates(intx,inty)this.x=x;this.y=y;第5章 消息、继承与多态/SquareCoordinatesShapesclassSquareextendsCoordinatesimplementsShapespublicintwidth,height;publicdoublegetArea()return(width*height);publicdoublegetPerimeter()return(2*width+2*height);publicSquare(intx,inty,intwidth,intheight)super(x,y);this.width=width;this.height=height;第5章 消息、继承与多态classTriangleextendsCoordinatesimplementsShapespublicintwidth,height;publicdoublec;publicdoublegetArea()return(0.5*width*height);publicdoublegetPerimeter()return(width+height+c);publicTriangle(intx,inty,intbase,intheight)super(x,y);width=base;this.height=height;c=Math.sqrt(width*width+height*height);第5章 消息、继承与多态classCircleextendsCoordinatesimplementsShapespublicintwidth,height;publicdoubler;publicdoublegetArea()return(r*r*Math.PI);publicdoublegetPerimeter()return(2*Math.PI*r);publicCircle(intx,inty,intwidth,intheight)super(x,y);this.width=width;this.height=height;r=(double)width/2.0;第5章 消息、继承与多态publicclassc5_20extendsAppletSquareBox=newSquare(5,15,25,25);Triangletri=newTriangle(5,50,8,4);CircleOval=newCircle(5,90,25,25);publicvoidpaint(Graphicsg)g.drawRect(Box.x,Box.y,Box.width,Box.height);g.drawString(BoxArea:+Box.getArea(),50,35);g.drawString(BoxPerimeter:+Box.getPerimeter(),50,55);g.drawString(triArea:+tri.getArea(),50,75);g.drawString(triPerimeter:+tri.getPerimeter(),50,95);g.drawOval(Oval.x,Oval.y,Oval.width,Oval.height);g.drawString(ovalArea:+Oval.getArea(),50,115);运行结果与示例程序c5_19.java的相同。第5章 消息、继承与多态5.5.3 包与程序复用包与程序复用前面已介绍过,Java语言提供了很多包,例如,Java.io、Java.awt、Javalang等,这些包中存放着一些常用的基本类,如System类、String类、Math类等,它们被称为Java类库中的包。使用这些包使我们的编程效率大大提高。读者不妨想一想,直接使用Java类库中Math.sqrt()方法求解任意非负实数的平方根与自己动手编写这个程序,那个效率高?在许多场合反复使用那些早已编写好的,且经过严格测试的程序的技术被称为软件复用,在面向对象的程序设计中称为对象复用。第5章 消息、继承与多态对象复用是面向对象编程的主要优点之一,它是指同一对象在多个场合被反复使用。在Java语言中,对象是类的实例,类是创建对象的模板,对象是以类的形式体现的。因此,对象复用也就体现在类的重用上。利用面向对象技术开发一个实际的系统时,编程人员通常需要定义许多类共同工作,且有些类可能要在多处反复使用。在Java程序中,如果要想使一个类在多个场合下反复使用,可以把它存放在一个称之为“包”的程序组织单位中。可以说,包是接口和类的集合,或者说包是接口和类的容器。使用包有利于实现不同程序间类的重用。Java语言为编程人员提供了自行定义包的机制。第5章 消息、继承与多态包的作用有两个:一是划分类名空间,二是控制类之间的访问。这就需要我们注意下述两点。首先,既然包是一个类名空间,所以,同一个包中的类(包括接口)不能重名,不同包中的类可以重名。第二,类之间的访问控制是通过类修饰符来实现的,若类声明修饰符为public,则表明该类不仅可供同一包中的类访问,也可以被其他包中的类访问。若类声明无修饰符,则表明该类仅供同一包中的类访问。第5章 消息、继承与多态1. 创建包创建包包的创建就是将源程序文件中的接口和类纳入指定的包。在一般情况下Java源程序的构成由四部分组成:(1)一个包(package)说明语句(可选项)。其作用是将本源文件中的接口和类纳入指定包。源文件中若有包说明语句,必须是第一个语句。(2)若干个(import)语句(可选项)。其作用是引入本源文件中需要使用的包。(3)一个public的类声明。在一个源文件中只能有一个public类。(4)若干个属于本包的类声明(可选)。第5章 消息、继承与多态包的声明语句格式:package包名;利用这个语句就可以创建一个具有指定名字的包,当前.java文件中的所有类都被放在这个包中。例如下面的语句是合法的创建包的语句:packageshape;packageshape.shapeCircle;第5章 消息、继承与多态创建包就是在当前文件夹下创建一个子文件夹,存放这个包中包含的所有类的.class文件。packageshape.shapeCircle;语句中的符号“”代表了目录分隔符,说明这个语句创建了两个文件夹:第一个是当前文件夹下的子文件夹shape;第二个是shape下的子文件夹shapeCircle,当前包中的所有类就存放在这个文件夹里。若源文件中未使用package,则该源文件中的接口和类位于Java的无名包中(无名包又称缺省包),它们之间可以相互引用非private的数据成员或成员方法。无名包中的类不能被其他包中的类引用和复用。第5章 消息、继承与多态【示例程序c5_21.java】改写示例程序c5_20.java,将接口与类纳入包。第一步,建立5个源文件,文件的接口与类都属于包shape。设这些文件都存入当前文件夹d:myjava中。(1)名为Shapes.java的文件为:packageshape;publicinterfaceShapesabstractdoublegetArea();abstractdoublegetPerimeter();第5章 消息、继承与多态(2)名为Coordinates.java的文件为:packageshape;classCoordinatespublicintx,y;publicCoordinates(intx,inty)this.x=x;this.y=y;第5章 消息、继承与多态(3)名为Square.java的文件为:packageshape;publicclassSquareextendsCoordinatesimplementsShapespublicintwidth,height;publicdoublegetArea()return(width*height);publicdoublegetPerimeter()return(2*width+2*height);publicSquare(intx,inty,intwidth,intheight)super(x,y);this.width=width;this.height=height;第5章 消息、继承与多态(4)名为Triangle.java的文件为:packageshape;publicclassTriangleextendsCoordinatesimplementsShapespublicintwidth,height;publicdoublec;publicdoublegetArea()return(0.5*width*height);publicdoublegetPerimeter()return(width+height+c);publicTriangle(intx,inty,intbase,intheight)super(x,y);width=base;this.height=height;c=Math.sqrt(width*width+height*height);第5章 消息、继承与多态第二步,对上面的5个文件进行编译。d:myjavajavac-dd:myjavaShapes.javad:myjavajavac-dd:myjavaCoordinates.javad:myjavajavac-dd:myjavaTriangle.javad:myjavajavac-dd:myjavaSquare.javad:myjavajavac-dd:myjavaCircle.java在编译过程中,若不存在d:myjavashape文件夹,则系统自动创建此文件夹。编译命令中的“-d”是编译开头,其作用是通知javac将编译好的类文件(.class)存入文件夹d:myjavashape中。第5章 消息、继承与多态第三步,shape包的引用。建立一个引用程序:c5_21.java,并将该程序存放在d:myjava文件夹中。packagep2;importjava.awt.*;importjava.applet.*;importshape.*;/引入我们创建的包publicclassc5_21extendsApplet第5章 消息、继承与多态SquareBox=newSquare(5,15,25,25);Triangletri=newTriangle(5,50,8,4);CircleOval=newCircle(5,90,25,25);publicvoidpaint(Graphicsg)g.drawRect(Box.x,Box.y,Box.width,Box.height);g.drawString(BoxArea:+Box.getArea(),50,35);g.drawString(BoxPerimeter:+Box.getPerimeter(),50,55);g.drawString(triArea:+tri.getArea(),50,75);g.drawString(triPerimeter:+tri.getPerimeter(),50,95);g.drawOval(Oval.x,Oval.y,Oval.width,Oval.height);g.drawString(ovalArea:+Oval.getArea(),50,115);第5章 消息、继承与多态使用下述命令编译该程序:d:myjavajavac-dd:myjavac5_21.java对其编译时,javac从环境变量CLASSPATH(类似于DOS操作系统中的PATH,它指明所有缺省的类字节码文件路径)提供的路径去搜索名为shape的子目录,找到后引用该文件夹下的“.class”文件。编译结束后,在文件夹d:myjavap2下产生c5_21.class文件。由于类属于p2包,在执行c5_21.class时应在c5_21.class前加包名和小数点。见下面两行列出的c5_21.html文件内容。运行结果与c5_20.java相同。第5章 消息、继承与多态2包的引用包的引用将类组织成包的目的是为了更好地利用包中的类。通常一个类只能引用与它在同一个包中的类。如果需要使用其他包中的public类,则可以使用如下的几种方法。(1)在引入的类前加包名。一个类要引用其他类有两种方式:一是对于同一包中的其他类可直接引用,如c5_18.java中的引用。二是对于不同包中的其他类引用时需在类名前加包名,例如,若你的源文件中要引用包shape中的类Circle,可在源文件中的Circle之前加“shape.”,如shape.Circlec=newshape.Circle(25,25,5,3)。这种类名前加包名的引用方式适用于在源文件中使用少的情况。第5章 消息、继承与多态(2)用import关键字加载需要使用的类。上面的方法使用起来比较麻烦,还可以在当前程序中利用import关键字加载需要使用的类,这样在程序中引用这个类的地方就不需要再使用包名作为前缀。例如上面的语句在程序开始处增加了:importshape.Circle;/在程序开始加载其他包的类语句之后,就可以直接写成:Circlec=newCircle(25,25,5,3);第5章 消息、继承与多态(3)用import关键字加载整个包。上面的方法利用import语句加载了其他包中的一个类。若希望引入整个包也可以直接利用import语句。加载整个包的import语句可以写为:importshape.*;importjava.awt.*;上面第一个语句见c5_21.java例,第二个语句在前面的例子中已多次使用,它的作用是把Java系统有关抽象窗口工具的包(系统类库)加载到当前程序中。与加载单个类相同,加载整个包后,凡是用这个包中的类,都不需要再使用包名前缀。第5章 消息、继承与多态(4)设置CLASSPATH环境变量。包是一种组织代码的有效手段,包名实际上就指出了程序中需要使用的.class文件的所在之处。另一个能指明.class文件夹所在结构的是环境变量CLASSPATH。当一个程序找不到它所需使用的其他类的.class文件时,系统会自动到CLASSPATH环境变量所指明的路径中去寻找。设置CLASSPATH环境变量可以通过编辑系统的AUTOEXEC.BAT文件,或使用相关的DOS命令。例如下面的语句SETCLASSPATH=c:jdk1.3.1libtools.jar;第5章 消息、继承与多态将c:jdk1.3.1libtools.jar设置为当前CLASSPATH目录。对于JavaApplication程序,还可以通过为Java解释器设置参数来指定类文件路径。例如,对于JDK中的Java解释器java.exe,有开关参数-classpath。假设当需要解释执行的test.class文件不在当前目录而在C盘的TEMP目录下时,可以使用如下的命令行语句javatest-classpathc:temp来运行这个程序。
收藏 下载该资源
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号