Java继承机制文档格式.docx
《Java继承机制文档格式.docx》由会员分享,可在线阅读,更多相关《Java继承机制文档格式.docx(20页珍藏版)》请在冰豆网上搜索。
-使用protected及friendly访问修饰符。
-描述构造函数及初始化操作。
-在一个Java程序中,使用:
-重载方法
-覆盖方法
-父类构造函数
1.子类
在编程中,常常要创建某件事的模型(如:
一个职员),然后需要一个该基本模型的更专业化的版本。
比如,可能需要一个经理的模型。
显然经理实际上是一个职员,只是一个带有附加特征的职员。
看看下面的例子:
publicclassEmployee{
publicStringname=“”;
publicdoublesalary;
publicDatebirthDate;
publicStringgetDetail(){
System.out.println(“ThisisEmployee”);
}
publicclassManager{
publicStringdepartment;
publicStringgetDetail(){...}
在Manager和Employee类之间有许多相同的数据,而且有同时适用于Employee和Manager两者的方法。
在面向对象的语言中,允许程序员用现有的类来定义一个新类,这个新类就叫做原由类的子类。
如下所示,可用关键字extends来实现:
publicclassManagerextendsEmployee{
privateStringdepartment;
System.out.println(“ThisisManager”);
通过继承的方法,Manager类被定义,具有Employee所拥有的全部变量及方法。
所有这些变量和方法都是从父类的定义中继承来的。
所有的程序员需要做的是定义额外特征或规定将适用的变化。
注意:
这种方法是在维护和可靠性方面的一个伟大进步。
如果在Employee类中进行修改,那么,Manager类就会自动修改,而不需要程序员做任何工作,除了对它进行编译。
2.单继承性
Java编程语言允许一个类仅能扩展成一个其它类。
这个限制被称做单继承性。
Java编程语言加强了单继承性限制而使代码更为可靠。
使用单继承性的子类的一个例子如图5-1所示:
图5-1单继承
3.构造函数不能被继承
尽管一个子类从父类继承所有的方法和变量,但它不继承构造函数,掌握这一点很重要。
一个类能得到构造函数,只有两个办法。
或者写构造函数,或者根本没有写构造函数,类有一个缺省构造函数。
4.多态
将经理描述成职员不只是描述这两个类之间的关系的一个简便方法。
回想一下,经理类具有父类职员类的所有属性、和方法。
这就是说,任何在Employee上的合法操作在Manager上也合法。
如果Employee有raiseSalary()和fire()两个方法,那么Manager类也有。
一个对象只有一个类型(是在构造时给它的)。
但是,既然变量能指向不同类型的对象,那么变量就是多态性的。
在Java编程语言中,有一个类,它是其它所有类的父类。
这就是java.lang.object类。
因此,实际上,以前的定义被简略为:
publicclassEmployeeextendsObject
publicclassManagerextendsEmployee
Object类定义许多有用的方法,包括toString(),它就是为什么Java软件中每样东西都能转换成字符串表示法的原因。
象大多数面向对象语言一样,Java允许父类的变量引用子类的对象。
因此,下面代码是合法的:
Employeee=newManager()
使用变量e是因为,你能访问的对象部分只是Employee的一个部分;
Manager的特殊部分是隐藏的。
这是因为编译者应意识到,e是一个Employee,而不是一个Manager。
因而,下述情况是不允许的:
e.department="
Finance"
;
//illegal
4.1.异类收集
可以创建具有共同类的对象的收集(如数组)。
这种收集被称作同类收集。
Java编程语言有一个Object类,因此,由于多态性,它能收集所有种类的元素。
这种收集被称作异类收集。
创建一个Manager并慎重地将其引用赋到类型Employee的变量中似乎是不现实的。
但这是可能的,而且有很多为什么要取得这种效果的理由。
用这种方法,可以写出一个能接受通用对象的方法,在这种情况下,就是类Employee,并在它的任何子类的对象上正确地运作。
然后可以在应用类中产生一个方法,该应用类抽取一个职员,并将它的薪水与某个阈值进行比较来决定该职员的纳税责任。
利用多态性,可以做到这些:
//IntheEmployeeclass
publicTaxRatefindTaxRate(Employeee){
//docalculationsandreturnataxratefore
//Meanwhile,elsewhereintheapplicationclass
Managerm=newManager();
TaxRatet=findTaxRate(m);
这是合法的,因为一个经理就是一个职员。
异类收集就是不相同的对象的收集。
在面向对象语言中,可以创建许多东西的收集。
所有的都有一个共同的祖先类-Object类。
如:
Employee[]staff=newEmployee[1024];
staff[0]=newManager();
staff[1]=newEmployee();
staff[2]=newEngineer();
for(){
staff[i].getDetail()
甚至可以写出一个排序的方法,它将职员按年龄或薪水排序,而忽略其中一些人可能是经理。
4.2.Instanceof运算符
我们可以通过异类收集将一些不同对象收集到它们的父类中。
有时你可能想知道某个对象到底属于哪个类。
这就是instanceof运算符的目的。
假设类层次按照下列方法被扩展:
publicclassEmployeeextendsObject
publicclassContractorextendsEmployee
如果你通过Employee类型的引用接受一个对象,它是否能够变换成Manager或Contractor,可以象这样用instanceof来测试:
publicvoidmethod(Employeee){
if(einstanceofManager){
//Getbenefitsandoptionsalongwithsalary
}elseif(einstanceofContractor){
//Gethourlyrates
}else{
//regularemployee
}
4.3.对象的类型转换
在你接收父类的一个引用时,你可以通过使用Instanceof运算符判定该对象实际上是你所要的子类,并可以用类型转换该引用的办法来恢复对象的全部功能:
if(einstanceofManager){
Managerm=(Manager)e;
System.out.println("
Thisisthemanagerof"
+m.department);
//restofoperation
如果不用强制类型转换,那么引用e.department的尝试就会失败,因为编译器不能将被称做department的成员定位在Employee类中。
如果不用instanceof做测试,就会有类型转换失败的危险。
通常情况下,类型转换一个对象引用的尝试是要经过几种检查的:
向上强制类型转换类层次总是允许的,而且事实上不需要强制类型转换运算符。
可由简单的赋值实现。
对于向下类型转换,编译器必须满足类型转换至少是可能的这样的条件。
比如,任何将Manager引用类型转换成Contractor引用的尝试是肯定不允许的,因为Contractor不是一个Manager。
类型转换发生的类必须是当前引用类型的子类。
如果编译器允许类型转换,那么,该引用类型就会在运行时被检查。
比如,如果instanceof检查从源程序中被省略,而被类型转换的对象实际上不是它应被类型转换进去的类型,那么,就会发生一个运行时错误(exception)。
异常是运行时错误的一种形式,而且是后面模块中的主题。
5.访问控制
变量和方法可以处于四个访问级别的一个中;
公共,受保护,缺省或私有。
类可以在公共或缺省级别。
变量、方法或类有缺省访问性,如果它没有显式受保护修饰符作为它的声明的一部分的话。
这种访问性意味着,访问可以来自任何方法,当然这些方法只能在作为目标的同一个包中的成员类当中。
以修饰符protected标记的变量或方法实际上比以缺省访问控制标记的更易访问。
一个protected方法或变量可以从类当中的任何方法进行访问,这个类可以是同一个包中的成员,也可以是从任何子类中的任何方法进行访问。
当它适合于一个类的子类但不是不相关的类时,就可以使用这种受保护访问来访问成员。
表6-1访问性标准
修饰符同类同包子类通用性
public(公共)是是是是
protected(保护)是是是
friendly(缺省)是是
private(私有)是
受保护访问甚至被提供给子类,该子类驻留在与拥有受保护特征的类的不同包中。
6.重载
6.1.重载方法名称
在某些情况下,可能要在同一个类中写几个方法做基本相同工作,区别仅仅在于参数不同。
例如,输出文本参数的方法,println(Strings)。
很有可能,你还需要打印int,float,double等类型数据的打印方法。
这是合情合理的,因为各种数据类型要求不同的格式,而且可能要求不同的处理。
你当然可以分别创建三个方法:
printInt(),printFloat()和printdouble()。
但是Java为你提供了更好的办法。
Java允许多个方法使用相同的名称,而通过参数表来加以区分,这就叫重载。
对于上述三种打印方法,可以使用相同的函数名,在参数的数量和类型上对此进行区分:
publicvoidprintln(inti)
publicvoidprintln(floatf)
publicvoidprintln(doubled)
当写代码来调用这些方法中的一种方法时,根据提供的参数的类型选择合适的一种方法。
有两个规则适用于重载方法:
●调用语句的参数表必须有足够的不同,以便区分出正确的方法被调用。
正常的拓展晋升(如,单精度类型float到双精度类型double)可以被应用,但是这样会导致在某些条件下的混淆。
●重载方法的参数表必须不同。
方法的返回类型可以各不相同,但它不足以使返回类型变成唯一的差异。
6.2.重载构造函数
如果有一个类带有几个构造函数,那么也许会想复制其中一个构造函数的某些功能到另一个构造函数中。
可以通过使用关键字this作为一个方法调用来达到这个目的。
privateStringname;
privateintsalary;
publicEmployee(Stringn,ints){
name=n;
salary=s;
publicEmployee(Stringn){
this(n,0);
publicEmployee(){
this("
Unknown"
);
在第二个构造函数中,有一个字符串参数,调用this(n,0)将控制权传递到构造函数的另一个版本,即采用了一个String参数和一个int参数的构造函数中。
在第三个构造函数中,它没有参数,调用this(“Unknownn”)将控制权传递到构造函数的另一个版本,即采用了一个String参数的构造函数中。
注:
对于this的任何调用,如果出现,在任何构造函数中必须是第一个语句。
7.覆盖方法
7.1.覆盖方法
我们可以通过继承来获得父类的属性和方法。
在此基础上,我们不仅可以在子类中增加属性和方法,而且还可以修改父类的方法。
如果在新类中定义一个方法,其名称、返回类型及参数表正好与父类中方法的名称、返回类型及参数相匹配,那么,新方法被称做旧方法的覆盖方法。
考虑一下在Employee和Manager类中的这些方法的例子:
Stringname;
intsalary;
publicStringgetDetails(){
return"
Name:
"
+name+"
\n"
+"
Salary:
+salary;
Stringdepartment;
Managerof"
+department;
Manager类有一个定义的getDetails()方法,因为它是从Employee类中继承的。
基本的方法被子类的版本所代替或覆盖了。
假设例子及下述方案是正确的:
Employeee=newEmployee();
如果请求e.getDetails()和m.getDetails,就会调用不同的行为。
Employee对象将执行与Employee有关的getDetails版本,Manager对象将执行与Manager有关的getDetails()版本。
不明显的是如下所示:
Employeee=newManager();
e.getDetails();
事实上,你得到与变量的运行时类型(即,变量所引用的对象的类型)相关的行为,而不是与变量的编译时类型相关的行为。
这是面向对象语言的一个重要特征。
它也是多态性的一个特征,并通常被称作虚拟方法调用。
在前例中,被执行的e.getDetails()方法来自对象的真实类型,Manager。
7.2.调用覆盖方法
覆盖方法的规则:
●覆盖法的名称以及覆盖法参数的顺序必须与父类中的方法的名称以及参数的顺序相同。
●覆盖方法的返回类型必须与它所覆盖的方法相同。
●覆盖方法不能比它所覆盖的方法访问性差
●覆盖方法不能比它所覆盖的方法抛出更多的异常。
(异常将在下一个模块中讨论。
)
这些规则源自多态性的属性和Java编程语言必须保证“类型安全”的需要。
考虑一下这个无效方案:
publicclassParent{
publicvoidmethod(){}
publicclassChildextendsParent{
privatevoidmethod(){}
publicclassUseBoth{
publicvoidotherMethod(){
Parentp1=newParent();
Parentp2=newChild();
p1.method();
p2.method();
Java编程语言语义规定,p2.method()导致方法的Child版本被执行,但因为方法被声明为private,p2(声明为Parent)不能访问它。
于是,语言语义冲突。
7.3.关键字super
关键字super可被用来引用该类中的超类。
它被用来引用超类的成员变量或方法。
通常当覆盖一个方法时,实际目的不是要更换现有的行为,而是要在某种程度上扩展该行为。
用关键字super可获得:
returnName:
\nSalary:
privateStringdepartment;
returnsuper.getDetails()+"
\nDepartment:
请注意,super.method()格式的调用,如果对象已经具有父类类型,那么它的方法的整个行为都将被调用,也包括其所有副面效果。
该方法不必在父类中定义。
它也可以从某些祖先类中继承。
7.4.调用父类构造函数
象普通方法一样,构造函数也可以调用直接父类的非private构造函数。
通常,你会定义一个带参数的构造函数,并希望使用这些参数来控制一个对象的父类部分的构造函数。
你可以通过从子类构造函数的第一行调用关键字super的手段调用一个特殊的父类构造函数作为子类初始化的一部分。
要控制具体的构造函数的调用,必须给super()提供合适的参数。
当调用不带参数的super时,缺省的父类构造函数(即,带0个参数的构造函数)被隐含地调用。
在这种情况下,如果没有缺省的父类构造函数,将导致编译错误。
例如:
publicclassEmployee{
publicManager(Strings,Stringd){
super(s);
//CallparentconstructorwithStringargument
department=d;
当使用super或this时,它们必须被放在构造函数的第一行。
显然,两者不能被放在一个单独行中,但这种情况事实上不是一个问题。
如果写一个构造函数,它既没有调用super(…)也没有调用this(…),编译器自动插入一个调用到父类构造函数中,而不带参数。
其它构造函数也能调用super(…)或this(…),调用一个Static方法和构造函数的数据链。
最终发生的是父类构造函数(可能几个)将在链中的任何子类构造函数前执行。
7.5.构造并初始化对象
对象初始化是一个相当复杂的过程。
首先,系统为整个对象分配存储空间,并为实例变量分配默认值。
第二步,调用顶层构造函数。
递归调用父类的构造函数。
例如,我们用下面的代码初始化Manager和Employee类。
PublicclassObject{
…
publicObject(){}
…
publicclassEmployeeextendsObject{
privateStringname;
privatedoublesalary=15000.00;
privateDatebirthDate;
publicEmployee(Stringn,DateDoB){
//implicitsuper();
name=n;
birthDate=DoB;
}
publicEmployee(Stringn){
this(n,null);
publicclassManagerextendsEmployee{
privateStringdepartment;
publicManager(Stringn,Stringd){
super(n);
department=d;
8.Object类
Object类是Java语言中所有类的根。
如果声明一个类的时候没有使用extends子句,编译器会自动加上“extendsObject”子句。
也就是说,下面两个声明是等价的:
publicclassEmployee{
这样的技术,允许你从Object类中继承并覆盖几个很有用的方法。
8.1.==运算符与equals()方法
==运算符进行等值比较。
也就是说,对于任何引用值X和Y,当且仅当X和Y指向同一对象时,X==Y返回真。
Java.lang包中的Object类有publicbooleanequals(Objectobj)方法。
它也比较两个对象是否相等。
仅当被比较的两个引用指向同一对象时,对象的equals()方法返回true。
Object类的equals()方法很少被使用,因为,多数情况下我们希望比较两个对象的内容,而不是判断两个引用是否指向同一对象。
String类中的覆盖equals()方法返回true,当且仅当参数是一个不为null的String对象,该对象与调用该方法的String对象具有相同的字符顺序。
Strings1=newString(“JDK1.2”);
Strings2=newString