第5章 消息、继承与多态1

来源:互联网 发布:linux安装控制面板 编辑:程序博客网 时间:2024/05/17 07:11
第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】
class student
{ public String name;
public char sex;
public int no;
public int age;
student(int cno,String cname, char csex, int cage)
{ name=cname;
sex=csex;
no=cno;
age=cage;
}
第5章 消息、继承与多态
public void showNo( ){System.out.println("No:"+no);}
public void showName( ){System.out.println("Name:"+name);}
public void showSex( ){System.out.println("Sex:"+sex);}
public void showAge( ){System.out.println("age:"+age);}
}
class studentScore
{ private int no;
private double score;
public void sendScore(int cno,double cscore)
{ //下面两句是对象发送给自身的消息,要求给自己的数据成员赋值,
//这是一种私有消息,外界是不知道的。
no=cno;
score=cscore;
}
第5章 消息、继承与多态
void printScore( ){System.out.println("No:"+no+" score:"+score);}
}
public class c5_1
{
public static void main(String[ ] args)
{ int m;
//下面两句发送new消息给类student,要求创建类student的对象st1,st2
student st1=new student(101,"zhang li",'F',18);
student st2=new student(102,"hong bing",'M',17);
//发送new消息给类studentScore,要求创建类studentScore的对象sc1,sc2
studentScore sc1=new studentScore( );
studentScore sc2=new studentScore( );
第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:101
Name:zhang li
age:18
m=20
No:102
Name:hong bing
No:101 score:97.0
No:102 score:84.0
第5章 消息、继承与多态
5.2
访



一个类总能够访问自己的数据成员和成员方法。但是,其他
类是否能访问这个类的数据成员或成员方法,是由该类的访问控
制符及该类数据成员和成员方法的访问控制符决定的。这就是
说,访问控制符是一组限定类、数据成员或成员方法是否可以被
其他类访问的修饰符。类的访问控制符只有public一个,缺省访
问控制符时具有“友好访问”的特性。数据成员和成员方法的访问
控制符有public、private、protected和缺省访问控制符等几种。见
表5.1。
第5章 消息、继承与多态
表5.1
类、数据成员和成员方法的访问控制符及其作用

数据成员与方法
Public 缺省
public 所有类包中类(含当前类)
protected 包中类(含当前类),所有子类包中类(含当前类)
缺省(friendly) 包中类(含当前类) 包中类(含当前类)
private 当前类本身当前类本身
第5章 消息、继承与多态
5.2.1
公共访问控制符public
Java的类是通过包的概念来组织的,简单地说,定义在同一
个程序文件中的所有类都属于同一个包。处于同一个包中的类都
是可见的,即可以不需任何说明而方便地互相访问和引用。而对
于不同包中的类,一般说来,它们相互之间是不可见的,当然也
不可能互相引用。然而,当一个类被声明为public时,只要在其
他包的程序中使用import语句引入这个public类,就可以访问和引
用这个类,创建这个类的对象,访问这个类内部可见的数据成员
和引用它的可见的方法。例如,Java类库中的许多类都是公共
类,我们在程序中就是通过import语句将其引入的。
第5章 消息、继承与多态
当一个类的访问控制符为public时,表明这个类作为整体对
其他类是可见和可使用的,这个类就具有了被其他包中的类访问
的可能性。但是,处于不同包中的public类作为整体对其他类是
可见的,并不代表该类的所有数据成员和成员方法也同时对其他
类是可见的,这得由这些数据成员和成员方法的修饰符来决定。
只有当public类的数据成员和成员方法的访问控制符也被声明为
public时,这个类的所有用public修饰的数据成员和成员方法也同
时对其他类是可见的。在程序设计时,如果希望某个类能作为公
共工具供其他的类和程序使用,则应该把类本身和类内的方法都
定义成public。例如,Java类库中的标准数学函数类math和标准数
学函数方法。
需要注意的是,数据成员和成员方法的访问控制符被声明为
public时,会造成安全性和封装性下降,所以一般应尽量少用。
第5章 消息、继承与多态
5.2.2
缺省访问控制符
如果一个类没有访问控制符,说明它具有缺省的访问控制特
性,这种缺省的访问控制特性称为“友好访问”。友好访问规定只
有在同一个包中的对象才能访问和引用这些类,因此,又称为
包访问性。同样道理,类内的数据成员和成员方法如果没有访
问控制符来限定,也具有“友好访问”的特性,它们也具有包访问
性,可以被同一个包中的其他类所访问和引用。
第5章 消息、继承与多态
【示例程序c5_2.java】 计算矩形面积。
class classArea
{
public double lon,wid;//数据成员的修饰符为public
public double area(double x,double y)// 成员方法的修饰符为public
{ double s; //方法内的变量
lon=x;
wid=y;
s=lon*wid; //求矩形面积
return s; //返回面积值
}
}
第5章 消息、继承与多态
public class c5_2
{
public static void main(String[ ] args)
{
double a=2.2,b=3.1,z;
/*在类c5_2中创建被访问类classArea的对象ss,ss可以访问类
classArea内的数据成员和成员方法。这就是说,同一包中的类是
可见的,可以互相引用*/
第5章 消息、继承与多态
classArea ss=new classArea( );
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章 消息、继承与多态
class classArea
{
double lon,wid; //缺省数据成员的修饰符
double area(double x,double y) // 缺省成员方法的修饰符
{ double s; //方法内的变量
lon=x;
wid=y;
s=lon*wid; //求矩形面积
return s; //返回面积值
}
}
第5章 消息、继承与多态
5.2.3
私有访问控制符private
用private修饰的数据成员或成员方法只能被该类自身所访问
和修改,而不能被任何其他类(包括该类的子类)来访问和引用。
它提供了最高的保护级别。当其他类希望获取或修改私有成员
时,需要借助于类的方法来实现。
【示例程序c5_3.java】 用private修饰的数据成员。
class P1
{ private int n=9; //私有数据成员n
int nn;
P1( )//构造方法
{ nn=n++; //可以被该类的对象自身访问和修改 }
第5章 消息、继承与多态
void ma( )
{ System.out.println("n="+n);// 可以被该类的对象自身访问 }
}
public class c5_3 extends P1// 类class c5_3是类P1的子类
{
public static void main(String[ ] args)
{
P1 m1=new P1( );
System.out.println("m1.nn="+m1.nn);
// System.out.println("m1.n="+m1.n); 错,不能引用父类的私有成员
m1.ma( ); //可以引用P1类自身的成员方法
}
}
运行结果:
m1.nn=9 n=10
第5章 消息、继承与多态
5.2.4
保护访问控制符protected
用protected修饰的成员变量可以被三种类引用:该类自身、
与它在同一个包中的其他类、在其他包中的该类的子类。使用
protected修饰符的主要作用是允许其他包中的它的子类来访问父
类的特定属性。
【示例程序c5_4.java】
class classArea
{
private double lon,wid; // private修饰的私有数据成员
protected double area(double x,double y)
// protected修饰的成员方法
第5章 消息、继承与多态
{ double s;
lon=x;
wid=y;
s=lon*wid;
return s;
}
}
public class c5_4 //类c5_4与类classArea在一个包中
{
public static void main(String[ ] args)
{ double a=2.2,b=3.1,z;
第5章 消息、继承与多态
/*在类c5_4中创建被访问类classArea的对象ss,ss可以访问
类classArea内的成员方法,因为该方法是protected修饰的。但不
能访问该类的数据成员,因为它们是用private修饰的私有数据
成员。*/
classArea ss=new classArea( );
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】 加法重载的例子。
import java.awt.*;
import java.applet.*;
public class c5_5 extends Applet
{
int add(int a,int b) //重载的方法1
{ return(a+b);}
double add(double x,double y) //重载的方法2
{ return(x+y); }
double add(double x,double y, double z) //重载的方法3
{ return(x+y+z); }
public void paint(Graphics g)
{ g.drawString("Sum is:"+add(8.5,2.3),5,10);
g.drawString("Sum is:"+add(21,38),5,30);
g.drawString("Sum is:"+add(8.5,2.3,8.5+2.3),5,50);
}
}
第5章 消息、继承与多态
运行结果如下:
Sum is: 10.8
Sum is: 59
Sum is: 21.6
该类中定义了三个名为add的方法:第一个方法是计算两个
整数的和;第二个方法是计算两个浮点数的和;第三个方法是
计算三浮点数据的和。编译器根据方法引用时提供的实际参数
选择执行对应的重载方法。
第5章 消息、继承与多态
5.3.3
覆盖
由于面向对象系统中的继承机制,子类可以继承父类的方
法。但是,子类的某些特征可能与从父类中继承来的特征有所
不同,为了体现子类的这种个性,Java允许子类对父类的同名方
法重新进行定义,即在子类中定义与父类中已定义的相同名而
内容不同的方法。这种多态被称为覆盖(overload)。
由于覆盖的同名方法是存在于子类对父类的关系中,所以
只需在方法引用时指明引用的是父类的方法还是子类的方法,
就可以很容易地把它们区分开来。
第5章 消息、继承与多态
import java.io.*;
public class AutInheritance{
public static void main(String args[]){
subClass subW = new subClass();
subW.doPrint();
}
} class superClass{
int x;
superClass(){
x=3;
System.out.println("in superClass:x="+x);
}
void doPrint(){
System.out.println("in superClass.doPrint()");
}
}
第5章 消息、继承与多态
class subClass extends superClass{
int x;
subClass(){
super(); //调用superClass的构造方法
x=8;
System.out.println("in subClass() :x="+x);
}
void doPrint(){
super.doPrint(); //调用superClass方法
System.out.println("in subClass.doPrint()");
System.out.println("super.x="+super.x+" sub.x="+x);
}
}
第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】
class a1
{ int x=25;
private int z; //不能被子类继承的私有数据成员z
}
class c5_6 extends a1 //a1是c5_6的父类,c5_6是a1的子类
{
public static void main(String[ ] argS)
{ c5_6 p=new c5_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】
class a1
{ int x=8; } //父类中定义了数据成员x
class c5_7 extends a1
{ int x=24; //子类中也定义了数据成员x
public static void main(String[ ] args)
{ int s1,s2;
a1 p=new a1( ); //创建父类的对象p
c5_7 p1=new c5_7( ); //创建子类的对象p1
s1=p.x;
s2=p1.x; //子类对象引用自己的数据成员,把父类数据成员“隐藏”起来
System.out.println("s1="+s1);
System.out.println("s2="+s2);
}
}
运行结果:
s1=8 s2=24
第5章 消息、继承与多态
3
.成员方法的继承
子类可以继承父类的非私有成员方法。下面的程序说明这一问题。
【示例程序c5_8.java】
class a1
{ int x=0,y=1;
void Myp( )
{ System.out.println("x="+x+" y="+y); }
private void Printme( )
{ System.out.println("x="+x+" y="+y); }
}
第5章 消息、继承与多态
public class c5_8 extends a1
{
public static void main(String arg[ ])
{ int z=3;
c5_8 p1=new c5_8( );
p1.Myp( );
// p1.Printme( ); 错,不能继承父类的private方法
}
}
运行结果:
x=0 y=1
第5章 消息、继承与多态
4
.成员方法的覆盖(overload)
子类可以重新定义与父类同名的成员方法,实现对父类方法
的覆盖。方法的覆盖与数据成员的隐藏的不同之处在于:子类
隐藏父类的数据成员只是使之不可见,父类同名的数据成员在
子类对象中仍然占有自己的独立的内存空间;子类方法对父类
同名方法的覆盖将清除父类方法占用的内存,从而使父类方法
在子类对象中不复存在。
第5章 消息、继承与多态
【示例程序c5_9.java】
class a1
{ int x=10; int y=31;
public void Printme( )
{ System.out.println("x="+x+" y="+y);}
}
public class c5_9 extends a1
{ int z=35;
public void Printme( ) //子类中定义了与父类同名的成员方法,实现覆盖
{ System.out.println(" z="+z); }
public static void main(String arg[ ])
{ a1 p2=new a1( ); //创建父类对象p2
c5_9 p1=new c5_9( ); //创建子类对象p1
p1.Printme( ); //子类对象引用子类方法,覆盖了父类的同名方法
p2.Printme( ); //父类对象引用父类方法
}
}
第5章 消息、继承与多态
运行结果是:
z=35
x=10 y=31
方法的覆盖中需要注意的是:子类在重新定义父类已有的
方法时,应保持与父类完全相同的方法名、返回值类型和参数
列表,否则就不是方法的覆盖,而是子类定义自己特有的方
法,与父类的方法无关。
第5章 消息、继承与多态
5.4.4 this
与super
1
.this
的使用场合
在一些容易混淆的场合,例如,当成员方法的形参名与数
据成员名相同,或者成员方法的局部变量名与数据成员名相同
时,在方法内借助this来明确表示引用的是类的数据成员,而不
是形参或局部变量,从而提高程序的可读性。简单地说,this代
表了当前对象的一个引用,可将其理解为对象的另一个名字,
通过这个名字可以顺利地访问对象、修改对象的数据成员、调
用对象的方法。归纳起来,this的使用场合有下述三种:
第5章 消息、继承与多态
(1) 用来访问当前对象的数据成员,其使用形式如下:
this.数据成员
(2) 用来访问当前对象的成员方法,其使用形式如下:
this.成员方法(参数)
(3) 当有重载的构造方法时,用来引用同类的其他构造方
法,其使用形式如下:
this(参数)
第5章 消息、继承与多态
【示例程序c5_10.java】
class a1
{ int x=0;int y=1;
public void Printme( )
{ System.out.println("x="+x+" y="+y);
System.out.println("I am "+this.getClass( ).getName( ));
//用this来访问当前对象的成员方法,通过this表示当前对象来
打印当前对象的类名。其中
// getClass( )和getName( )是系统类库中提供的方法。
}
}
第5章 消息、继承与多态
public class c5_10 extends a1
{
public static void main(String arg[ ])
{ c5_10 p1=new c5_10( );
p1.Printme( );
}
}
运行结果如下:
x=0 y=1
I am c5_10
第5章 消息、继承与多态
【示例程序c5_11.java】
class classArea
{
double x,y;
double area(double x,double y)
{
double s;
this.x=x; // 借助this来表示引用的是类数据成员
y=y;//赋值号左边的y是数据成员,右边的y是参数,请复习4.1.9节
s=this.x*y; /*不能写成this.y,因为上一句没有这样写。如果写成
this.y,会产生什么结果?*/
return s;
}
}
第5章 消息、继承与多态
public class c5_11 extends classArea
{
public static void main(String[ ] args)
{
double a=2.2,b=3.1,z;
c5_11 ss=new c5_11( );//创建一个ss对象
z=ss.area(a,b);//引用父类对象的成员方法求面积
System.out.println("z="+z);
}
}
运行结果是:
z=6.820000000000001
第5章 消息、继承与多态
【示例程序c5_12.java】 计算圆的面积和周长。
public class c5_12
{
public static void main(String[ ] args)
{ double x;
circle cir=new circle(5.0);
x=cir.area( );
System.out.println("圆的面积="+x);
x=cir.perimeter( );
System.out.println("圆的周长="+x);
}
}
第5章 消息、继承与多态
class circle
{
double r; //定义半径
final double PI=3.14159265359; //定义圆周率
public circle(double r) //类的构造方法
{ this.r=r; }
double area( ) //计算圆面积的方法
{ return PI*r*r; } //通过构造方法给r赋值
double perimeter( ) //计算圆周长的方法
{
return 2*(this.area( )/r); //使用this变量获取圆的面积
}
}
第5章 消息、继承与多态
运行结果:
圆的面积=78.53981633974999
圆的周长=31.415926535899995
第5章 消息、继承与多态
2
.super
的使用场合
super
表示的是当前对象的直接父类对象,是当前对象的直
接父类对象的引用。所谓直接父类是相对于当前对象的其他“祖
先”类而言。例如,假设类A派生出子类B,类B又派生出自己的
子类C,则B是C的直接父类,而A是C的祖先类。super代表的就
是直接父类。若子类的数据成员或成员方法名与父类的数据成员
或成员方法名相同时,当要调用父类的同名方法或使用父类的同
名数据成员,则可用关键字super来指明父类的数据成员和方法。
super的使用方法有三种:
第5章 消息、继承与多态
(1) 用来访问直接父类隐藏的数据成员,其使用形式如下:
super.数据成员
(2) 用来调用直接父类中被覆盖的成员方法,其使用形式如下:
super.成员方法(参数)
(3) 用来调用直接父类的构造方法,其使用形式如下:
super(参数)
第5章 消息、继承与多态
【示例程序c5_13.java】
class a1
{
int x=4;int y=1;
public void Printme( )
{
System.out.println("x="+x+" y="+y);
System.out.println("class name: "+this.getClass( ).getName( ));
}
}
public class c5_13 extends a1
{
第5章 消息、继承与多态
int x;
public void Printme( )
{ int z=super.x+6; //引用父类(即a1类)的数据成员
super.Printme( ); //调用父类(即a1类)的成员方法
System.out.println("I am "+this.getClass( ).getName( ));
x=5;
System.out.println(" z="+z+" x="+x);//打印子类的数据成员
}
第5章 消息、继承与多态
public static void main(String arg[ ])
{ int k;
a1 p1=new a1( );
c5_13 p2=new c5_13( );
p1.Printme( );
p2.Printme( );
// super.Printme( );//错,在static方法中不能引用非static成员方法
// k=super.x+23;//错,在static方法中不能引用非static数据成员
}
}
第5章 消息、继承与多态
运行结果如下:
x=4 y=1
class name: a1
x=4 y=1
class name: c5_13
I am c5_13
z=10 x=5
第5章 消息、继承与多态
5.4.5
构造方法的重载与继承
1
.构造方法的重载
一个类的若干个构造方法之间可以相互调用。当一个构造
方法需要调用另一个构造方法时,可以使用关键字this,同时这
个调用语句应该是整个构造方法的第一个可执行语句。使用关
键字this来调用同类的其他构造函数时,优点同样是可以最大限
度地提高对已有代码的利用程度,提高程序的抽象度和封装
性,减少程序的维护工作量。
第5章 消息、继承与多态
【示例程序c5_14.java】
class addclass
{
public int x=0,y=0,z=0;
//以下是多个同名不同参数的构造方法
addclass(int x) //可重载的构造方法1
{ this.x=x; }
addclass(int x,int y) //可重载的构造方法2
{
this(x);// 当前构造方法调用可重载的构造方法1
this.y=y;
}
第5章 消息、继承与多态
addclass(int x,int y,int z) //可重载的构造方法3
{
this(x,y);// 当前构造方法调用可重载的构造方法2
this.z=z;
}
public int add( )
{ return x+y+z;}
}
public class c5_14
{
第5章 消息、继承与多态
public static void main(String[ ] args)
{
addclass p1=new addclass(2,3,5);
addclass p2=new addclass(10,20);
addclass p3=new addclass(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=10
x+y=30
x=1
第5章 消息、继承与多态
2
.构造方法的继承
子类可以继承父类的构造方法,构造方法的继承遵循以下的
原则:
(1) 子类无条件地继承父类的不含参数的构造方法。
(2) 如果子类自己没有构造方法,则它将继承父类的无参数构
造方法作为自己的构造方法;如果子类自己定义了构造方法,则
在创建新对象时,它将先执行继承自父类的无参数构造方法,然
后再执行自己的构造方法。
(3) 对于父类的含参数构造方法,子类可以通过在自己的构造
方法中使用super关键字来调用它,但这个调用语句必须是子类构
造方法的第一个可执行语句。
第5章 消息、继承与多态
【示例程序c5_15.java】
class addclass
{
public int x=0,y=0,z=0;
addclass(int x) //父类可重载的构造方法1
{ this.x=x; }
addclass(int x,int y) //父类可重载的构造方法2
{ this.x=x; this.y=y; }
addclass(int x,int y,int z) //父类可重载的构造方法3
{ this.x=x; this.y=y; this.z=z; }
public int add( )
{ return x+y+z;}
}
第5章 消息、继承与多态
public class c5_15 extends addclass
{ int a=0,b=0,c=0;
c5_15(int x) //子类可重载的构造方法1
{ super(x);
a=x+7;
}
c5_15(int x,int y) //子类可重载的构造方法2
{ super(x,y);
a=x+5; b=y+5;
}
c5_15(int x,int y,int z) //子类可重载的构造方法3
{ super(x,y,z);
a=x+4; b=y+4; c=z+4;
}
第5章 消息、继承与多态
public int add( )
{
System.out.println("super: x+y+z="+super.add( ));
return a+b+c;
}
public static void main(String[ ] args)
{
c5_15 p1=new c5_15(2,3,5);
c5_15 p2=new c5_15(10,20);
c5_15 p3=new c5_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=10
a+b+c=22
super: x+y+z=30
a+b=40
super: x+y+z=1
a=8
第5章 消息、继承与多态
5.4.6
向方法传递对象
前面已讲过传递给方法的参数可以是表达式(如常量、变
量、对象)等,并说明传递给方法的参数若是变量,则只能由
实参传递给形参,而不能由形参带回,它是一种单向值传递。
也就是说,在方法的引用过程中,对于形参变量值的修改并不
影响实参变量的值。但是,传递给方法的参数若是对象,则方
法可以对其做永久性修改。
第5章 消息、继承与多态
【示例程序c5_16.java】
import java.awt.*;
import java.applet.*;
class student
{ public String Name;
public int age=16;
public int score=0;
public void ShowStudent(Graphics g,int x,int y)
{ g.drawString("Name:"+Name,x,y);
g.drawString("age:"+age,x,y+20);
g.drawString("score:"+score,x,y+40);
}
}
第5章 消息、继承与多态
public class c5_16 extends Applet
{
public void studentAttributes(student s,
String Name, int age, int score)
{ s.Name=Name; s.age=age;
s.score=score;
}
第5章 消息、继承与多态
public void paint(Graphics g)
{ student st1=new student( ); //创建对象st1
student st2=new student( ); //创建对象st2
studentAttributes(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】
class SuperClass //定义父类
{ int x=1,y=2,t=98;
void show( )
{ System.out.println("x+y="+(x+y)); }
}
class SubClass extends SuperClass //定义子类
{ int y=9,z=7;
void show( )
{ System.out.println("y*z="+(y*z)); }
}
第5章 消息、继承与多态
public class c5_17 //使用父类与子类
{
public static void main(String[ ] args)
{ SuperClass sc,scf; //声明父类对象
SubClass sb,sbf; //声明子类对象
sc=new SuperClass( );
sb=new SubClass( );
System.out.println("sc.x="+sc.x+" sc.y="+sc.y+" sc.t="+sc.t);
//System.out.println(" sc.z="+sc.z); 错,父类对象不能引用子类数据成员z
sc.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不可引用z
System.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=1 sc.y=2 sc.t=98
x+y=3
sb.y=9 sb.z=7 sb.t=98
y*z=63
scf.x=1 scf.y=2
scf.show( ): y*z=63
sbf.x=1 sbf.y=9 sbf.z=7
sbf.show( ): y*z=63
第5章 消息、继承与多态
5.4.8
继承与封装的关系
在面向对象系统中,有了封装机制以后,对象之间只能通过
消息传递进行通信。那么,继承机制的引入是否削弱了对象概念
的封装性?继承和封装是否矛盾?其实这两个概念并没有实质性
的冲突,在面向对象系统中,封装性主要指的是对象的封装性,
即将属于某一类的一个具体的对象封装起来,使其数据和操作成
为一个整体。
在引入了继承机制的面向对象系统中,对象依然是封装得很
好的实体,其他对象与它进行通讯的途径仍然只有一条,那就是
发送消息。类机制是一种静态机制,不管是基类还是派生类,对
于对象来说,它仍然是一个类的实例,既可能是基类的实例,也
可能是派生类的实例。因此继承机制的引入丝毫没有影响对象的
封装性。
第5章 消息、继承与多态
从另一角度看,继承和封装机制还具有一定的相似性,它
们都是一种共享代码的手段。继承是一种静态共享代码的手
段,通过派生类对象的创建,可以接受某一消息,启动其基类
所定义的代码段,从而使基类和派生类共享了这一段代码。封
装机制所提供的是一种动态共享代码的手段,通过封装,我们
可将一段代码定义在一个类中,在另一个类所定义的操作中,
我们可以通过创建该类的实例,并向它发送消息而启动这一段
代码,同样也达到共享的目的。
原创粉丝点击