第3章继承及内部类本章主要内容1.类的继承性2.内部类3.类的多态性4.接口5.包3.1类的继承性使用继承,可以从另一个类,称为基类(base)或者超类(superclass),派生出一个类,称为派生类(derivedclass)或者子类(subclass)。所有子类可以访问超类的非私有成员,而且可以添加它们自己的成员。它们能够覆盖(override)超类的非私有成员,将它们替换为自己的代码。3.1.1父类和子类子类以两个或者更多超类为基础被称为多重继承(multipleInheritance),但Java并不支持多重继承(c++支持)。我们只能够对一个类使用关键字extends。这里要使用关键字implements来实现。就是通过关键字implements将别的类作为接口(interface)来实现,可以实现任意数量的接口。声明格式:声明子类的格式:[修饰词]class子类名extends父类名可见,在类的声明语句中加入extends关键字,然后指定父类名即可通过继承父类声明一个子类。在类的声明语句中加入extends关键字和指定的类名即可实现类的继承,例如:publicclassMyAppletextendsjava.applet.AppletpublicclassMyApplicationextendsFramepublicclassMyApp1extendsObjectpublicclassMyApp2第一条语句声明子类MyApplet的父类是Applet,指明Applet的层次结构;第二条语句声明子类MyApplication的父类是Frame;第三条语句声明子类MyApp1的父类是Object;第四条语句等价于publicclassMyApp2extendsObject。类的继承的模拟图图反映的内容上图反映了Java类的层次结构。最顶端的类是Object,是所有类的始祖。一个类可以有多个子类,也可以没有子类,但它必定有一个父类(Object除外)。子类不能继承父类中的private成员。另一方面,对继承的理解应该扩展到整个父类的分支,也就是说,子类继承的成员实际上是整个父系的所有成员。结论:子类只能有一个父类。如果省略了extends,子类的父类是Object。子类继承了父类和祖先的成员,可以使用这些成员。在需要的时候,子类可以添加新的成员变量和方法,也可以隐藏父类的成员变量或覆盖父类的成员方法。3.1.2成员变量的继承和隐藏所谓隐藏是指子类重新定义了父类中的同名变量。子类执行自己的方法时,操作的是子类的变量,子类执行父类的方法时,操作的是父类的变量。在子类中要特别注意成员变量的命名,防止无意中隐藏了父类的关键成员变量,这有可能给你的程序带来麻烦。例3.1下面的三个程序说明从点Point类扩展到线Line类和圆Circle类的方法,这是三个公共类,不能放在同一个文件中。publicclassPoint{protectedintx,y;Point(inta,intb){setPoint(a,b);}publicvoidsetPoint(inta,intb){x=a;y=b;}publicintgetX(){returnx;}publicintgetY(){returny;}}publicclassLineextendsPoint{protectedintx,y,endX,endY;Line(intx1,inty1,intx2,inty2){setLine(x1,y1,x2,y2);}publicvoidsetLine(intx1,inty1,intx2,inty2){x=x1;y=y1;endX=x2;endY=y2;}ⅠpublicintgetX(){returnx;}publicintgetY(){returny;}publicintgetEndX(){returnendX;}publicintgetEndY(){returnendY;}publicdoublelength(){returnMath.sqrt((endX-x)*(endX-x)+(endY-y)*(endY-y));}}ⅡpublicclassCircleextendsPoint{protectedintradius;Circle(inta,intb,intr){super(a,b);setRadius(r);}publicvoidsetRadius(intr){radius=r;}publicintgetRadius(){returnradius;}publicdoublearea(){return3.14159*radius*radius;}}ⅢPoint的成员x,y//受保护的成员变量,代表点的坐标Point//点的构造方法setPoint//设定点的坐标值的方法getX,getY//返回坐标x和y的值的方法Line的成员x,y,endX,endY//子类受保护的成员变量,代表线的两个端点坐标Line//线的构造方法setLine//设定线的两个端点坐标值的方法getX,getY//返回起点坐标x和y的值的方法getEndX,getEndY//返回终点坐标endX和endY的值的方法length//返回线的长度的方法x,y//继承父类的受保护成员变量,但被子类隐藏setPoint//继承父类的方法getX,getY//继承父类的方法,但被子类覆盖Circle的成员radius//子类受保护的成员变量,代表圆的半径Circle//圆的构造方法setRadius//设定半径值的方法getRadius//返回半径值的方法area//返回圆面积的方法x,y//继承父类的受保护成员变量setPoint//继承父类的方法getX,getY//继承父类的方法使用final终止继承finalclassanimal{publicvoidbreathe(){System.out.println(Breathing...);}}上面将类标记为final,将不能从此类继承创建子类,否则出错。3.1.3成员方法的覆盖通过继承,子类可以继承父类中所有可以被子类访问的成员方法,但如果子类的方法与父类方法同名,则不能继承,此时称子类的方法覆盖了父类的那个方法,简称为方法覆盖。方法的覆盖(override),为子类提供了修改父类成员方法的能力。下面的例子显示了在子类Circle中添加toString方法,用来返回圆半径和圆面积信息。例3.2对Object的toString方法的覆盖。结果如图所示。classCircle{privateintradius;Circle(intr){setRadius(r);}publicvoidsetRadius(intr){radius=r;}publicintgetRadius(){returnradius;}publicdoublearea(){return3.14159*radius*radius;}publicStringtoString(){return圆半径:+getRadius()+圆面积:+area();}}publicclassO1{publicstaticvoidmain(Stringargs[]){Circlec=newCircle(10);System.out.println(\n+c.toString());}}程序中改写了上一节介绍的Circle,添加了toString方法并修改了它的返回值。由于toString和继承下来的方法名相同、返回值类型相同,因此就覆盖了父类中的toString方法。而父类Point中的toString方法尽管没有显示出来,它仍然存在,它是从Object中继承下来的。覆盖方法classanimal{publicvoidbreathe(){System.out.println(Breathing...);}}classfishextendsanimal{publicvoidbreathe()//覆盖基类的breathe方法{System.out.println(Bubbling...);}}publicclassapp{publicstaticvoidmain(String[]args){System.out.println(Creatingananimal...);animala=newanimal();a.breathe();System.out.println();System.out.println(Creatingalungfish...);fishf=newfish();f.breathe();}}输出结果Creatingananimal...Breathing...Creatingalungfish...Bubbling...方法覆盖时要特别注意:用来覆盖的子类方法应和被覆盖的父类方法保持同名、同返回值类型,以及相同的参数个数和参数类型。如果被覆盖的方法没有声明抛出异常,子类的覆盖方法可以有不同的抛出异常子句。有时,可能不需要完全覆盖一个方法,可以部分覆盖一个方法。部分覆盖是在原方法的基础上添加新的功能,即在子类的覆盖方法中添加一条语句:super.原父类方法名,然后加入其它语句。使用final终止覆盖classanimal{finalvoidbreathe(){System.out.println(Breathing...);}}上面将方法标记为final,以后该方法将不能够覆盖,否则出错。3.1.4this、super和super()例3.3this和super的使用。运行结果如图所示。classPoint{protectedintx,y;Point(inta,intb){setPoint(a,b);}publicvoidsetPoint(inta,intb){x=a;y=b;}}classLineextendsPoint{protectedintx,y;Line(inta,intb){super(a,b);setLine(a,b);}publicvoidsetLine(intx,inty){this.x=x+x;this.y=y+y;}publicdoublelength(){intx1=super.x,y1=super.y,x2=this.x,y2=this.y;returnMath.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));}publicStringtoString(){return直线端点:[+super.x+,+super.y+][+x+,+y+]直线长度:+this.length();}}publicclassThisDemo{publicstaticvoidmain(Stringargs[]){Lineline=newLine(50,50);System.out.println(\n+line.toString());}}this和super的作用:(1)this实际代表的是当前类或对象本身。在一个类中,this表示对当前类的引用,在使用类的成员时隐含着this引用,尽管可以不明确地写出,例如length和toString中对x和y的使用。当一个类被实例化为一个对象时,this就是对象名的另一种表示。通过this可顺利地访问对象,凡在需要使用对象名的地方均可用this代替。使用this关键字Java对象包含一个名为this的数据成员,就是对当前对象的引用classData//该例Data类有printData方法,通过把当前对象传递给{privateStringdata_string;//另一个对象的print方法,它输出当Data(Strings){data_string=s;}//前对象的数据。publicStringgetData(){returndata_string;}publicvoidprintData(){printerp=newprinter();p.prin