第5章Java类的继承抽象类和接口.docx
- 文档编号:25793094
- 上传时间:2023-06-14
- 格式:DOCX
- 页数:35
- 大小:105.83KB
第5章Java类的继承抽象类和接口.docx
《第5章Java类的继承抽象类和接口.docx》由会员分享,可在线阅读,更多相关《第5章Java类的继承抽象类和接口.docx(35页珍藏版)》请在冰豆网上搜索。
第5章Java类的继承抽象类和接口
第5章类的继承、包及接口
上一章我们介绍了面向对象程序设计的基本概念,如类的定义、对象的创建(实例化)、类的成员等。
本章将继续介绍类的继承性、类的访问限定、抽象类、匿名类以及包和接口等概念。
5.1类的继承
面向对象的重要特点之一就是继承。
类的继承使得能够在已有的类的基础上构造新的类,新类除了具有被继承类的属性和方法外,还可以根据需要添加新的属性和方法。
继承有利于代码的复用,通过继承可以更有效地组织程序结构,并充分利用已有的类来完成复杂的任务,减少了代码冗余和出错的几率。
5.1.1类继承的实现
1.问题的提出
在介绍类继承的实现之前,我们先看一下上一章介绍的Citizen(公民)类和ResultRegister(成绩登记)类,分析一下它们之间的关系。
Citizen类的完整代码如下:
/**这是一个公民类的定义
*类名:
Citizen
*/
importjava.util.*;
publicclassCitizen
{
//以下声明成员变量(属性)
Stringname;
Stringalias;
Stringsex;
Datebrithday;//这是一个日期类的成员变量
Stringhomeland;
StringID;
//以下定义成员方法(行为)
publicStringgetName()//获取名字方法
{//getName()方法体开始
returnname;
}//getName()方法体结束
/***下边是设置名字方法***/
publicvoidsetName(Stringname)
{//setName()方法体开始
this.name=name;
}//setName()方法体结束
/***下边是列出所有属性方法***/
publicvoiddisplayAll()
{//displayAll()方法体开始
System.out.println("姓名:
"+name);
System.out.println("别名:
"+alias);
System.out.println("性别:
"+sex);
if(brithday==null)brithday=newDate(0);
System.out.println("出生:
"+brithday.toString());
System.out.println("出生地:
"+homeland);
System.out.println("身份标识:
"+ID);
}displayAll()方法体结束
publicvoiddisplay(Stringstr1,Stringstr2,Stringstr3)//重载方法1
{
System.out.println(str1+""+str2+""+str3);
}
publicvoiddisplay(Stringstr1,Stringstr2,Dated1)//重载方法2
{
System.out.println(str1+""+str2+""+d1.toString());
}
publicvoiddisplay(Stringstr1,Stringstr2,Dated1,Stringstr3)//…3
{
System.out.println(str1+""+str2+""+d1.toString()+""+str3);
}
publicCitizen(Stringname,Stringalias,Stringsex,Datebrithday,Stringhomeland,StringID)//带参数构造方法
{
this.name=name;
this.alias=alias;
this.sex=sex;
this.brithday=brithday;
this.homeland=homeland;
this.ID=ID;
}
publicCitizen()//无参构造方法
{
name="无名";
alias="匿名";
sex="";
brithday=newDate();
homeland="";
ID="";
}
}
ResultRegister类的代码如下:
/*
*这是一个学生入学成绩登记的简单程序
*程序的名字是:
ResultRegister.java
*/
importjavax.swing.*;
publicclassResultRegister
{
publicstaticfinalintMAX=700;//分数上限
publicstaticfinalintMIN=596;//分数下限
Stringstudent_No;//学号
intresult;//入学成绩
publicResultRegister(Stringno,intres)//构造方法
{
Stringstr;
student_No=no;
if(res>MAX||res { str=JOptionPane.showInputDialog("请核对成绩: ",String.valueOf(res)); result=Integer.parseInt(str); } elseresult=res; }//构造方法结束 publicvoiddisplay()//显示对象属性方法 { System.out.println(this.student_No+""+this.result); }//显示对象属性方法结束 } 通过上一章对上述两类的介绍和示例演示,我们可以分析一下,在Citizen类中,定义了每个公民所具有的最基本的属性,而在ResultRegister类中,只定义了与学生入学成绩相关的属性,并没有定义诸如姓名、性别、年龄等这些基本属性。 在登录成绩时,我们只需要知道学生号码和成绩就可以了,因为学生号码对每一个学生来说是唯一的。 但在有些时候,诸如公布成绩、推荐选举学生干部、选拔学生参加某些活动等,就需要了解学生更多的信息。 如果学校有些部门需要学生的详细情况,既涉及到Citizen类中的所有属性又包含ResultRegister中的属性,那么我们是定义一个包括所有属性的新类还是修改原有类进行处理呢? 针对这种情况,如果建立新类,相当于从头再来,那么就和前边建立的Citizen和ResultRegister类没有什么关系了。 这样做有违于面向对象程序设计的基本思想,也是我们不愿意看到的,因此我们应采用修改原有类的方法,这就是下边所要介绍的类继承的实现。 2.类继承的实现 根据上边提出的问题,要处理学生的详细信息,已建立的两个类Citizen和ResultRegister已经含有这些信息,接下来的问题是在它们之间建立一种继承关系就可以了。 从类别的划分上,学生属于公民,因此Citizen应该是父类,ResultRegister应该是子类。 下边修改ResultRegister类就可以了。 定义类的格式在上一章已经介绍过,不再重述。 将ResultRegister类修改为Citizen类的子类的参考代码如下: /* *这是一个学生入学成绩登记的简单程序 *程序的名字是: ResultRegister.java */ importjava.util.*; importjavax.swing.*; publicclassResultRegisterextendsCitizen { publicstaticfinalintMAX=700;//分数上限 publicstaticfinalintMIN=596;//分数下限 Stringstudent_No;//学号 intresult;//入学成绩 publicResultRegister() { student_No="00000000000"; result=0; } publicResultRegister(Stringname,Stringalias,Stringsex,Datebrithday,Stringhomeland,StringID,Stringno,intres)//构造方法 { this.name=name; this.alias=alias; this.sex=sex; this.brithday=brithday; this.homeland=homeland; this.ID=ID; Stringstr; student_No=no; if(res>MAX||res { str=JOptionPane.showInputDialog("请核对成绩: ",String.valueOf(res)); result=Integer.parseInt(str); } elseresult=res; }//构造方法结束 publicvoiddisplay()//显示对象属性方法 { displayAll(); System.out.println("学号="+student_No+"入学成绩="+result); }//显示对象属性方法结束 } 在上边的类定义程序中,着重显示部分是修改添加部分。 可以看出,由于它继承了Citizen类,所以它就具有Citizen类所有的可继承的成员变量和成员方法。 下边我们写一个测试程序,验证修改后的ResultRegister的功能。 例5.1测试ResultRegister类的功能。 程序参考代码如下: /*这是一个测试ResultRegister类的程序 *程序的名字是: TestExam5_1.java */ importjava.util.*; publicclassTestExam5_1 { publicstaticvoidmain(String[]args) { ResultRegisters1,s2,s3;//声明对象s1,s2,s3 s1=newResultRegister("丽柔","一刀","女",newDate("12/30/88"),"上海","421010198812302740","200608010201",724);//创建对象s1 s2=newResultRegister("李明","","男",null,"南京","50110119850624273x","200608010202",657);//创建对象s2 s3=newResultRegister(); s3.display();//显示对象s1的属性 System.out.println("============================"); s2.display();//显示对象s2的属性 System.out.println("============================"); s1.display();//显示对象s3的属性 System.exit(0);//结束程序运行,返回到开发环境 } } 编译、运行程序,在程序执行过程中,由于生成对象s1时传递的成绩724超出了上限700,所以就出现了如图5-1的超限处理对话框,修正成绩后,按“确定”按钮确认,之后输出如图5-2的执行结果。 至此,我们已经完成了类继承的实现。 但是,还有一些问题没有解决。 如上边所述,不同的管理部门可能需要了解学生的不同信息。 在上一章中,我们介绍了方法的重载,引用不同的重载方法来显示不同的属性信息。 那是在父类中实现的。 现在,我们要在子类中显示学生的不同的属性信息。 解决这一问题,仍然可以使用重载方法的方式,不过要采用重载方法和覆盖方法并举的方式。 下边将简要介绍覆盖方法的基本概念和覆盖方法的实现与应用。 5.1.2覆盖(Override)方法 所谓方法的覆盖(,就是指在子类中重写了与父类中有相同签名的方法。 这样做的好处是方法名一致易记易用,可以实现与父类方法不同的功能。 下边我们将ResultRegister中的display()更名为displayAll()并添加重写父类Citizen中的display()方法。 重写的方法代码如下: publicvoiddisplayAll()//重写displayAll() {//displayAll()方法体开始 super.displayAll();//引用父类方法 System.out.println("学号: "+student_No); System.out.println("入学成绩: "+result); }//displayAll()方法体结束 publicvoiddisplay(Stringstr1,Stringstr2,Stringstr3)//重写方法2 { super.display(str1,str2,str3);//引用父类方法 System.out.println(this.student_No+""+result); } publicvoiddisplay(Stringstr1,Stringstr2,Dated1)//重写方法3 { super.display(str1,str2,d1);//引用父类方法 System.out.println(this.student_No+""+result); } publicvoiddisplay(Stringstr1,Stringstr2,Dated1,Stringstr3)//…4 { super.display(str1,str2,d1,str3);//引用父类方法 System.out.println(this.student_No+""+result); } 注意: 在引用父类方法时,我们使用了super关键字。 在前边我们看到了this关键字的使用。 this代表当前对象对本类成员的引用;而super则代表当前对象对父类成员的引用。 下边举例说明覆盖方法的应用。 例5.2编写测试程序,测试ResultRegister类的覆盖方法。 程序参考代码如下: /*这是一个测试ResultRegister类覆盖方法的程序 *程序的名字是: TestExam5_2.java */ importjava.util.*; publicclassTestExam5_2 { publicstaticvoidmain(String[]args) { ResultRegisters1;//声明对象s1 s1=newResultRegister("丽柔","一刀","女",newDate(0),"上海","421010198812302740","200608010201",654);//创建对象s1 s1.displayAll();//显示对象s1的所有属性 System.out.println("============================"); s1.display(s1.ID,s1.name,s1.sex);//显示s1的身份标识,名字,性别及学号和成绩 System.out.println("============================"); s1.display(s1.name,s1.sex,s1.brithday);//显示s1的名字,性别,生日及学号和成绩 System.out.println("============================"); s1.display(s1.name,s1.sex,s1.brithday,s1.homeland);//显示s1的名字,性别,生日,出生地及学号和成绩 System.exit(0);//结束程序运行,返回到开发环境 } } 请读者编译、运行程序,分析程序的执行结果。 一般来说,在Java程序的编译过程中,当遇到一个对象引用方法时,系统首先在对象所在的类中寻找这个方法的签名,如果没有找到的话,系统会把查找传递到其父类中查找,如果仍然没找到,再转到父类的父类查找,依此下去,直到找到这个方法的签名为止。 如果查找到基类(最顶层)也没查到,系统将提示找不到此方法的错误信息。 5.1.3变量的隐藏(Hidded) 所谓变量的隐藏就是指在子类中定义的变量和父类中定义的变量有相同的名字或方法中定义的变量和本类中定义的变量同名。 事实上在前边介绍的类中已经遇到了这种情况。 诸如,在Citizen类构造方法的形式参数中,就定义了和本类中同名的变量。 在这种情况下,系统采用了局部优先的原则。 即在方法中,同名的方法变量优先,直接引用。 而成员变量(对象的属性)则被隐藏,需要引用时,应加上关键字this(本类)或super(父类)加以区分。 因此在Citizen类的构造函数中,我们看到了这样的引用语句: this.name=name;//将方法变量name的值赋给对象的属性name 在程序中对变量的引用时,什么情况下不需要加this、super? 什么情况下需要加,加哪个? 其规则如下: 1)当不涉及同名变量的定义时,对变量的引用不需要加this或super关键字。 2)当涉及同名变量的定义时,分两种情况: a)方法变量和成员变量同名,在引用成员变量时,前边加this; b)本类成员变量和父类成员变量同名,在引用父类成员变量时,前边加super。 变量的隐藏有点相似于方法的覆盖,也可以称为属性的覆盖。 只不过是为了区分是指变量而不是方法,用另一个名词“隐藏”称之而已。 有关其他的应用,我们在使用时再作介绍。 5.1.4应用示例 类的继承并不仅仅是把它们作为基础来定义新的类,实现类的重复使用。 它还可以通过方法覆盖实现动态的多态性操作(运行时多态)。 下边举一个简单的例子说明多态性操作。 例5.3: 定义一个Teacher类。 //Teacher.java publicclassTeacherextendsCitizen { publicStringteacherID;//声明教师代码 publicStringposition;//教师职位 publicStringcourseName;//主讲课程 //构造器 publicTeacher() { teacherID=”0000”; position=”助教”; courseName=”习题课“; } publicTeacher(StringpersonID,Stringname,Stringsex,Stringbirthday,StringteacherID,Stringposition,StringcourseName) { this.personID=personID; this.name=name; this.sex=sex; this.birthday=birthday; this.teacherID=teacherID; this.position=position; this.couseName=courseName; } publicvoiddisplay()//成员方法display() { super.display();//执行父类的显示方法 System.out.println(“教师代码: ”+teacherID); System.out.println(“教师职位: ”+position); System.out.println(“主讲课程: ”+courseName); } } 完成该类的定义之后,还需要一个程序使用这些类并对它们进行测试,测试程序如下: 例5.4: 对上边创建的类的覆盖方法进行多态性测试。 //polymorphismDemo.java publicclasspolymorphismDemo { publicstaticvoidmain(String[]args) { Citizen[]citizenObj={newStudent(“65410519700221275x”,”纪杨”,”男”,”1974.5.21”,”920150208”,”1992.9.1”,540),newTeacher(“65410519670221272x”,”姜氏”,”女”,”1967.2.21”,”1201”,”教授”,”物理学”)}; for(inti=1;i<=citizenObj.length;i++) { System.out.println(“第”+i+”次: ”); citizenObj[i-1].display();//显示对象信息 } } } 在测试程序中,生成了一个Citizen类型数组,各元素是其不同子类的对象,这样就可以使用一个Citizen类型的变量来引用子类的对象,循环调用不同类型对象的display()方法,如图5.2所示。 这是由两个子类的情况,当然在多个子类时,也可以产生随机下标,从数组中随机选择对象,这是一个多态性应用的简单例子。 大家可以参照例5.1编译、运行程序,思考一下程序的运行结果。 要使用多态性机制,必须具备如下条件: 1)子类对象的方法调用必须通过一个父类的变量进行。 比如上例的Citizen类型数组。 2)被调用的方法必须是覆盖的方法即在父类中也有该方法。 3)子类的方法特征与父类必须一致。 4)子类方法的访问权限不能比父类更严格。 5.2抽象类 类是对现实世界中实体的抽象,但我们不能以相同的方法为现实世界中所有的实体做模型,因为大多数现实世界的类太抽象而不能独立存在。 例如,我们熟悉的平面几何图形类,对于圆、矩形、三角形、有规则的多边形及其他具体的图形可以描述它的形状并根据相应的公式计算出面积来的。 那么任意的几何图形又如何描述呢? 它是抽象的,我们只能说它表示一个区域,它有面积。 那么面积又如何计算呢,我们不能够给出一个通用的计算面积的方法来,这也是抽象的。 在现实生活中,会遇到很多的抽象类,诸如交通工具类、鸟类等等。 本节我们简要介绍抽象类的定义和实现。 5.2.1抽象类的定义 在Java中所谓的抽象类,即是在类说明中用关键字abstract修饰的类。 一般情况下,抽象类中可以包含一个或多个只有方法声明而没有定义方法体的方法。 当遇到这样一些类,类中的某个或某些方法不能提供具体的实现代码时,可将它们定义成抽象类。 定义抽象类的一
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Java 继承 抽象 接口
![提示](https://static.bdocx.com/images/bang_tan.gif)