实验2 面向对象b继承多态c抽象类接口.docx

上传人:b****8 文档编号:9321082 上传时间:2023-02-04 格式:DOCX 页数:21 大小:23.84KB
下载 相关 举报
实验2 面向对象b继承多态c抽象类接口.docx_第1页
第1页 / 共21页
实验2 面向对象b继承多态c抽象类接口.docx_第2页
第2页 / 共21页
实验2 面向对象b继承多态c抽象类接口.docx_第3页
第3页 / 共21页
实验2 面向对象b继承多态c抽象类接口.docx_第4页
第4页 / 共21页
实验2 面向对象b继承多态c抽象类接口.docx_第5页
第5页 / 共21页
点击查看更多>>
下载资源
资源描述

实验2 面向对象b继承多态c抽象类接口.docx

《实验2 面向对象b继承多态c抽象类接口.docx》由会员分享,可在线阅读,更多相关《实验2 面向对象b继承多态c抽象类接口.docx(21页珍藏版)》请在冰豆网上搜索。

实验2 面向对象b继承多态c抽象类接口.docx

实验2面向对象b继承多态c抽象类接口

实验2面向对象(继承、多态、抽象类、接口)

注意:

有些程序由于Word的关系,复制后,tab缩位可能会变成其它符号。

需要你去调整一下,删除缩位,重新Tab

一、实验目的1

二、实验要求1

三、实验内容2

1.类的继承与覆盖练习2

1.0父类的哪些成员可以被继承?

2

1.1父类Student(学生)与子类HiStudent(大学生)2

1.2实例方法为什么需要覆盖4

1.3验证成员方法的覆盖方式5

1.4this、super和super()的使用6

1.5变量、静态方法的隐藏。

9

1.6隐藏与覆盖的综合性实例10

2.类的多态性练习11

2.1普通方法的重载(实验3,本实验略)11

2.2构造方法的重载(实验3,本实验略)11

2.3运行时多态与动态绑定11

3.抽象类13

4.接口14

一、实验目的

  通过编程和上机实验理解Java语言的面向对象特性,了解类的继承性和多态性的作用,掌握它们的实现方法,了解数据域和静态方法的隐藏,了解抽象类和接口的作用。

二、实验要求

1、编写体现类的继承性(成员变量、成员方法、成员变量隐藏)的程序;

2、编写体现类的多态性的程序;

3、编写体现抽象类和接口功能的程序。

三、实验内容

1.类的继承与覆盖练习

  例如,圆是一种形状。

圆类Circle是形状类Shape的子类。

父类也叫基类、超类,子类也叫次类、扩展类、派生类。

子类可从父类中产生,保留父类的成员变量和方法,并可根据需要对它们加以修改。

新类还可添加新的变量和方法。

这种现象就称为类的继承。

  当建立一个父类时,不必写出全部成员变量和成员方法。

只要简单地声明这个类是从一个已定义的类继承下来的,就可以引用被继承类的全部成员。

Java提供了一个庞大的类库让开发人员继承和使用。

设计这些类是出于公用的目的,因此,很少有某个类恰恰满足你的需要。

你必须设计自己的能处理实际问题的类,如果你设计的这个类仅仅实现了继承,和父类一样,那样没什么用。

所以,通常要对父类进行扩展,即添加新的属性和方法。

这使得子类要比父类大,但更具特殊性,代表着一组更具体的对象。

继承的意义就在于此。

1.0父类的哪些成员可以被继承?

可以访问的成员才能被继承。

具体分2种情况:

*当父类和子类在同一个包时,子类可以继承父类中的public/protected/无修饰级别的变量和方法;

*当父类和子类不在同一个包时,且父类是public的,那么子类可以继承父类的public/protected级别的变量和方法。

如果父类前没有public,那么子类无法访问父类,更谈不上继承的问题。

1.1父类Student(学生)与子类HiStudent(大学生)

(1)Student.java程序源代码如下。

publicclassStudent//学生类

{

protectedStringxm;//姓名

protectedintxh;//学号

voidsetData(Stringm,inth)//设置姓名和学号

{

xm=m;

xh=h;

}

publicvoidprint()//输出姓名和学号

{

System.out.println(xm+","+xh);

}

}

(2)HiStudent.java的描述和代码如下

程序功能:

通过Student类产生子类HiStudent,其不仅具有父类的成员变量xm(姓名)、xh(学号),还定义了新成员变量xy(学院)、xi(系)。

在程序中调用了父类的print方法,同时可以看出子类也具有该方法。

程序源代码如下

publicclassHiStudentextendsStudent{//大学生类继承自学生类

protectedStringxy;//所在学院或大学名称

protectedStringxi;//所在系的名称

publicstaticvoidmain(Stringargs[]){

Students1=newStudent();

s1.setdata("李四",12321);

s1.print();

HiStudents2=newHiStudent();

s2.setdata("张三",12345);//调用父类的成员方法

s2.xy="XX大学";//访问本类的成员变量

s2.xi="计算机系";//访问本类的成员变量

s2.print();//调用父类的方法,就象调用它自己的一样

System.out.print(s2.xm+","+s2.xy+","+s2.xi);

}

}

(3)编译并运行,结果如下图所示。

1.2实例方法为什么需要覆盖

上例中,s2.print();语句调用父类的方法,只能输出父类的数据(姓名和学号)。

如果想用函数输出姓名、学号、学院、系,需要在子类中另外添加一个函数。

如果这个函数也叫print(),(嗯,这样好记),并且如果形参、返回类型都一样,并且其可见性修饰符和父类print()方法的一样甚至更开放,并且它们都不是静态的,并且它所抛出的异常必须和被覆盖方法的所抛出的异常一致,或者是其子类,或者什么也不抛出,那么,我们可以说子类的print()方法覆盖(override)了父类的print()方法。

子类中添加的print()可以这样写:

publicvoidprint()//输出姓名、学号、学院、系

{

System.out.println(xm+","+xh+","+xy+","+xi);

}

可见,子类的覆盖方法重写(修改)了父类的被覆盖方法。

实现了不同的功能。

当然,父类的被覆盖方法仍然存在,在子类中可以用super.print()来引用。

嗯,如果参数不一样,那也叫重载。

如果返回类型不一样,或者一个静态一个非静态,或者子类方法的开放性比父类被覆盖方法的更低,都会编译错。

如果两个方法其它一样,又都是静态的,这没问题,叫做隐藏,而非覆盖。

另外,父类中用final修饰的方法不能被覆盖。

再重复一下方法覆盖的概念:

如果在子类中定义一个方法,其名称、返回类型及参数签名正好与父类中某个方法的名称、返回类型及参数签名相匹配,那么可以说,子类的方法覆盖了父类的方法。

覆盖的说明:

∙子类的方法名称返回类型及参数签名必须与父类的一致

∙子类方法不能缩小父类方法的访问权限

∙子类方法不能抛出比父类方法更多的异常

∙方法覆盖只存在于子类和父类之间,同一个类中只能重载

∙父类的静态方法不能被子类覆盖为非静态方法

∙子类可以定义于父类的静态方法同名的静态方法,以便在子类中隐藏父类的静态方法(满足覆盖约束),  

∙Java虚拟机把静态方法和所属的类绑定,而把实例方法和所属的实例绑定。

∙父类的非静态方法不能被子类覆盖为静态方法

∙父类的私有方法不能被子类覆盖

∙父类的抽象方法可以被子类通过两种途径覆盖(即实现和覆盖)

∙父类的非抽象方法可以被覆盖为抽象方法

1.3验证成员方法的覆盖方式

  方法覆盖为子类提供了修改父类实例方法的能力。

例如,Object类的toString方法返回的字符串是“类名@随机码”这样的。

子类可以修改层层继承下来的Object根类的toString方法,让它输出一些更有用的信息。

下面的程序显示了在子类Circle中添加toString方法,用来返回圆半径和圆面积信息。

(1)编写Circle类,覆盖Object类的toString方法,Circle类和Test类源代码如下。

classCircle{

privateintradius;

Circle(intr){

setRadius(r);

}

publicvoidsetRadius(intr){

radius=r;

}

publicintgetRadius(){

returnradius;

}

publicdoublearea(){

returnMath.PI*radius*radius;

}

publicStringtoString(){//覆盖继承自Object的toString()方法

return"圆半径:

"+getRadius()+"圆面积:

"+area();

}

}

publicclassTest{

publicstaticvoidmain(Stringargs[]){

Circlec=newCircle(10);

System.out.println(c.toString());

}

}

1.4this、super和super()的使用

(1)程序功能:

说明this.、this()、super.和super()的用法。

程序首先定义Point(点)类,然后创建点的子类Line(线)。

Line继承了Point的点,它自己又定义了一个点。

最后通过Test类输出线段的长度。

Line对象程序中通过super(a,b)调用父类Point的构造方法为父类的x和y赋值。

在子类Line的setLine方法中,因为参数名和成员变量名相同,为给成员变量赋值,使用this引用,告诉编译器是为当前类的成员变量赋值。

在length和toString方法中使用父类成员变量时,使用super引用,告诉编译器使用的是父类的成员变量。

(2)程序源代码如下。

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;//this.可省略

returnMath.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));

}

publicStringtoString(){

return"直线端点:

["+super.x+","+super.y+"]["+x+","+y+"]直线长度:

"+this.length();

}

}

publicclassTest{

publicstaticvoidmain(Stringargs[]){

Lineline=newLine(50,50);

System.out.println(line.toString());

}

}

说明:

这个例子只为验证super(),super.,this.等的用法。

当然,线并不是一种点,所以用Line继承Point不合适。

用聚合的形式更合适些。

例如可以如下修改:

classPoint{

protectedintx,y;

Point(intx,inty){

setPoint(x,y);

}

publicvoidsetPoint(intx,inty){

this.x=x;

this.y=y;

}

}

classLine{

Pointa,b;

publicdoublelength(){

returnMath.sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));

}

publicLine(){}

publicLine(Pointa,Pointb){

this.a=a;

this.b=b;

}

publicLine(intx1,inty1,intx2,inty2){

a=newPoint(x1,y1);

b=newPoint(x2,y2);

}

}

publicclassTest{

publicstaticvoidmain(Stringargs[]){

Lineline=newLine(1,1,2,0);

System.out.println("直线端点:

["+line.a.x+","+line.a.y+"]-["+line.b.x+","+line.b.y

+"]直线长度:

"+line.length());

}

}

1.5变量、静态方法的隐藏。

父类和子类定义了同名的变量,或者相同的静态方法。

(1)父类和子类有相同的变量。

父类变量被隐藏。

将B类中的x所在行屏蔽起来或者不屏蔽,比较输出结果有何变化。

classA{

intx=1;

}

classBextendsA{

//intx=2;

voidprintX(){//输出父类对象的x值

System.out.println(super.x);//如果B类没有声明x,那么super.可省略。

但如果B类也声明了变量x,那么父类的x被隐藏,要访问父类对象的成员,super.不能省略

}

}

publicclassTest{

publicstaticvoidmain(String[]s){

Bp=newB();

System.out.println(p.x);

p.printX();

//System.out.println(p.super.x);//语法错误。

要用super.x引用B类的父类对象的成员,可以在B类中进行

}

}

(2)父类和子类有相同的静态方法。

父类静态方法被隐藏。

下面B类中的print()方法,屏蔽或不屏蔽,输出结果有何不同?

classA{

staticvoidprint(){

System.out.println("A");

}

}

classBextendsA{

/*

staticvoidprint(){

System.out.println("B");

}

*/

}

publicclassTest{

publicstaticvoidmain(String[]s){

Bp=newB();

p.print();

}

}

【说明】上面A、B两类中的print()方法都是static的,称为静态方法隐藏。

如果都是非静态的,则称为覆盖。

如果其中一个是静态的,另一个非静态,则编译错,提示“无法覆盖”。

1.6隐藏与覆盖的综合性实例

注意看蓝色或红色的注释。

其中,红色的注释是容易出错的地方。

classA{

intx=1;//被隐藏

voidprint(){//被覆盖

System.out.println("这里是父类方法,x="+x);//父类A的方法中访问的变量必然是A类或A的父类的,不可能访问B类的。

m();//父类A的方法中调用的实例方法m()是子类B的,由于发生了覆盖

}

voidm(){//被覆盖

System.out.println("这里是父类的实例方法m()");

}

staticvoidm2(){//被隐藏

System.out.println("这里是父类的静态方法m2()");

}

}

classBextendsA{

intx=2;

voidprint(){

System.out.println("这里是子类方法,x="+x);//子类方法访问的变量是子类对象的(当然条件是子类中声明了这个变量)

System.out.println("这里是子类方法,super.x="+super.x);//super.x是父类对象的

super.print();//调用父类的print()方法

m();//调用本对象的m()方法

}

voidm(){

System.out.println("这里是子类的实例方法m()");

}

staticvoidm2(){

System.out.println("这里是子类的静态方法m2()");

}

}

publicclassTest{

publicstaticvoidmain(String[]s){

Ap=newB();

System.out.println(p.x);//通过引用变量p来访问变量或静态方法,要看p的声明类型。

所以x是A类的。

p.m2();//同上。

静态方法m2()是A类的。

p.print();//通过引用变量p来访问实例方法,要看p指向的对象的实际类型。

由于覆盖,调用的print()方法是子类的。

}

}

2.类的多态性练习

c++允许多重继承(多个直接父类),功能强大,但复杂的继承关系也给c++开发者带来了很大麻烦。

为了规避风险,java只允许单重继承,即,只能有一个直接父类,所以继承关系简单明了。

但在功能上又给开发者提出了难题。

因此,Java用多态性、抽象类、接口的概念来弥补此项不足。

多态性:

在程序中同一符号或名字在不同情况下具有不同解释

Ø编译时多态性:

指在程序编译阶段即可确定下来的多态性。

重载发生时,编译器根据方法签名来确定未来会调用哪个方法。

Ø运行时多态性:

指必须等到程序动态运行时才可确定的多态性,JVM根据引用变量指向的实际对象来绑定方法。

2.1普通方法的重载

(参考实验3,本实验略)

2.2构造方法的重载

(参考实验3,本实验略)

2.3运行时多态与动态绑定

一组方法,在多个类中都有定义。

由于继承与覆盖,到底调用哪个方法,需要根据不同的对象来进行动态绑定。

因此结果呈现多态性。

如果A是父类,B是子类,那么Ap=newB();是合法的。

p的类型被声明成A类型,但p实际上指向一个B类对象。

(1)如果A、B两类中都有实例方法m(),那么p.m()调用的是B中的m()。

(2)如果A中有而B中没有,那么p.m()调用的是B类对象从A继承而来的m()方法。

这是动态绑定。

(3)如果A中没有m()方法,则无论B中有没有,都会编译错。

因为编译并非执行,编译器发现p是A类型的,而A类型以及A的父类都没有m()方法,也就是说,A类没有这个方法,也没有通过继承获得这个方法。

另外,关于静态方法:

(1)如果m()方法在A、B两个类中,一个静态一个非静态,则编译错。

(无法覆盖)

(2)如果m()方法在A中是静态的,在B中没有,或者也是静态的,那么p.m()调用的是A类的静态方法。

编译的时候就把p.m()换成A.m()。

读1.6隐藏与覆盖的综合性实例。

(3)如果A中没有静态方法m(),那么编译器要看A的父类有没有。

静态方法虽然无法覆盖,但也可继承。

如果其父类也没有,则会编译错。

下面的程序,Test类中函数showInformation的形参是Person类型的引用变量x。

实参如果是其子类对象,那么就是让x指向子类对象,这和Ap=newB()的原理是一样的。

//代码Test.java

classPerson{

protectedStringname;//姓名

Person(){}

Person(Stringname){

this.name=name;

}

Stringinformation(){

return"类Person:

"+name;

}

}

classStudentextendsPerson{//学生类

protectedintid;//学号

Student(){}

Student(Stringname,intid){

super(name);

this.id=id;

}

/*

Stringinformation(){

return"类Student:

"+name+id;

}

*/

}

classHiStudentextendsStudent{//大学生类继承自学生类

protectedStringschool;//大学名称

protectedStringdepartment;//系的名称

HiStudent(){}

HiStudent(Stringname,intid,Stringschool,Stringdepartment){

super(name,id);

this.school=school;

this.department=department;

}

Stringinformation(){

return"类HiStudent:

"+name+id+school+department;

}

}

publicclassTest{

publicstaticvoidmain(String[]as){

showInformation(newPerson("张三"));

showInformation(newStudent("张三",201122));//Student类没有information方法,因此showInformation将调用Student对象继承来的information方法。

这是动态绑定。

showInformation(newHiStudent("张三",201122,"西南大学","计科系"));

}

staticvoidshowInformation(Personx){

System.out.println(x.information());

}

}

问题:

1、如果在Student类中,将对information()方法的屏蔽取消,结果有何变化?

2、Test类showInformation方法的形参类型是Person。

将其改为Object,编译能通过吗?

3.抽象类

将父类设计得非常抽象,让它包含所有子类的共同属性、方法,以至于它没有具体的实例。

如果子类不是抽象类,就必须实现(覆盖)抽象父类中的所有抽象方法。

这其实也就规定了子类必须实现的的共同行为。

abstractclassGeometricShape{//几何形状类

publicabstractdoublegetArea();//不知道具体形状,无法计算,只能定义

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 表格模板 > 书信模板

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1