1、第五章 继承、多态、重载与接口,2,主要内容,Java的继承 Java的多态 Java的覆盖 Java 的重载 构造方法的重载与继承 内部类与匿名类 接口,3,继承的概念,继承是存在于面向对象程序中的两个类之间的一种关系 当一个类获取另一个类中所有非私有数据和操作的定义作为自己的部分或全部成分时,就称这两个类之间具有继承关系 被继承的类称为父类或超类,继承了父类或超类的所有数据和操作的类称为子类 一个父类可以同时拥有多个子类 父类实际上是所有子类的公共域和公共方法的集合 每一个子类则是父类的特殊化,是对公共域和方法在功能、内涵方面的扩展和延伸,Java的继承,4,继承的概念,各种电话卡类及其间
2、的继承关系,Java的继承,5,单重继承与多重继承,单重继承指任何一个类都只有一个单一的父类 多重继承是指一个类可以有一个以上的父类,它的数据属性和操作从所有这些父类中继承 单重继承的程序结构比较简单,支持多重继承的程序,其结构则是复杂的网状,设计、实现都比较复杂 C+是开发人员熟悉的支持多重继承的面向对象的编程语言,Java语言出于安全、可靠性的考虑,仅支持单重继承,Java的继承,6,Java的派生子类,Java中的继承是通过extends关键字来实现的,在定义类时使用extends关键字指明新定义类的父类,就在两个类之间建立了继承关系 定义形式 class SubClass extend
3、s SuperClass 如果没有extends子句,则该类默认为java.lang.Object的子类 所以,Java中,所有的类都是通过直接或间接地继承java.lang.Object得到的 新定义的类称为子类,它可以从父类那里继承所有非private的属性和方法作为自己的成员,Java的继承,7,Java的派生子类,继承关系在UML图中,是用一个箭头来表示子类与父类的关系的 类Student从类Person继承,定义如下class Student extends Person /,Java的继承,8,派生子类实现电话卡类的继承结构,1: abstract class PhoneCard
4、2: 3: double balance; 4: 5: abstract boolean performDial( ); 6: double getBalance( ) 7: return balance; 10: 11: abstract class None-Number-PhoneCard extends PhoneCard 12: 13: String phoneSetType; 14: 15: String getSetType( ) 16: 17: return phoneSetType; 18: 19: ,Java的继承,9,派生子类实现电话卡类的继承结构,20: abstrac
5、t class Number-PhoneCard extends PhoneCard 21: 22: long cardNumber; 23: int password; 24: String connectNumber; 25: boolean connected; 26: 27: boolean performConnection(long cn,int pw) 28: 29: if(cn = cardNumber 36: 37: ,Java的继承,10,派生子类实现电话卡类的继承结构,38: class magCard extends None-Number-PhoneCard 39:
6、40: String usefulArea; 41: 42: boolean performDial( ) 43: 44: if( balance 0.9) 45: 46: balance -= 0.9; 47: return true; 48: 49: else 50: return false; 51: 52: ,Java的继承,11,派生子类实现电话卡类的继承结构,53: class IC-Card extends None-Number-PhoneCard 54: 55: boolean performDial( ) 56: 57: if( balance 0.5) 58: 59: b
7、alance -= 0.9; 60: return true; 61: 62: else 63: return false; 64: 65: ,Java的继承,12,派生子类实现电话卡类的继承结构,66: class IP-Card extends Number-PhoneCard 67: 68: Date expireDate; 69: boolean performDial( ) 70: 71: if( balance 0.3 78: 79: ,Java的继承,13,派生子类实现电话卡类的继承结构,80: class D200-Card extends Number-PhoneCard 8
8、1: 82: double additoryFee; 83: 84: boolean performDial( ) 85: 86: if( balance (0.5 + additoryFee ) 87: 88: balance -= (0.5 + additoryFee); 89: return true; 90: 91: else 92: return false; 93: 94: ,Java的继承,14,派生子类实现电话卡类的继承结构说明,代码定义了七个类 None-Number-PhoneCard类和Number-PhoneCard类是PhoneCard类派生出的子类 magCard类
9、和IC-Card类是None-Number-PhoneCard类派生出的子类 IP-Card类和D200-Card类是Number-PhoneCard类派生出的子类 程序中只有在第3句(PhoneCard类中)定义了域balance 但在第44,46(magCard类中),第57,59(IC-Card类中),第71,73(IP-Card类中),第86,88(D200-Card类中)句中都使用了balance域 它们自身并未定义balance域,使用的balance都是从父类PhoneCard那里继承来的,Java的继承,15,派生子类实现电话卡类的继承结构说明,PhoneCard类在第5句定义
10、了一个抽象方法performDial( ) 它的两个子类也是抽象类,可以不实现这个抽象方法,分别派生出来的4个电话卡类不是抽象类 故而分别定义了针对自己具体情况的performDial( )方法 第68句使用一个java.util包中的Java系统类Date,每个Date类的对象代表一个具体的日期 第71句中new Date( )表达式的作用是创建一个包含当前日期的Date类的对象 after( )方法是Date类的方法,在失效日期比当前日期晚时,expireDate.after(new Date( )返回true,否则返回false,Java的继承,16,域的继承,子类可以继承父类的所有非私
11、有域,例如各类电话卡类所包含的域分别为PhoneCard类: double balance;None-Number-PhoneCard类:double balance; /继承自父类PhoneCardString phoneSetType;Number-PhoneCard类:double balance; /继承自父类PhoneCardlong cardNumber;int password;String connectNumber;boolean connect;magCard类:double balance; /继承自父类None-Number-PhoneCardString phoneS
12、etType; /继承自父类None-Number-PhoneCardString usefulArea;,Java的继承,17,域的隐藏,子类重新定义一个与从父类那里继承来的域变量完全相同的变量,称为域的隐藏,80: class D200-Card extends Number-PhoneCard 81: 82: double additoryFee; 83: double balance; 84: boolean performDial( ) 85: 86: if( balance (0.5 + additoryFee ) 87: 88: balance -= (0.5 + additor
13、yFee); 89: return true; 90: 91: else 92: return false; 93: 94:,Java的继承,18,域的隐藏,第83句增加定义了一个与从父类那里继承来的balance变量完全相同的变量后,D200-Card类中的域变为D200-Card类double balance; /继承自父类Number-PhoneCarddouble balance; /D200-Card类自己定义的域long cardNumber; /继承自父类Number-PhoneCardint password; /继承自父类Number-PhoneCardString conn
14、ectNumber; /继承自父类Number-PhoneCardboolean connect; /继承自父类Number-PhoneCarddouble additoryFee;,Java的继承,域的隐藏,子类中定义了与父类同名的属性变量,即出现了子类变量对同名父类变量的隐藏 所谓隐藏是指子类拥有了两个相同名字的变量,一个继承自父类,另一个由自己定义 当子类执行继承自父类的操作时,处理的是继承自父类的变量,而当子类执行它自己定义的方法时,所操作的就是它自己定义的变量,而把继承自父类的变量“隐藏”起来,20,域的隐藏举例,TestHiddenField.java,1: public clas
15、s TestHiddenField 2: 3: public static void main(String args) 4: 5: D200-Card my200 = new D200-Card( ); 6: my200.balance = 50.0; 7: System.out.println(父类被隐藏的金额为: +my200.getBalance( ); 8: if(my200.performDial( ) 9: System.out.println(子类的剩余金额为:+my200.balance); 10: 11: 12:abstract class PhoneCard 13: 14
16、: double balance;,Java的继承,21,域的隐藏举例,TestHiddenField.java,15: 16: abstract boolean performDial( ); 17: double getBalance( ) 18: 19: return balance; 20: 21: 22:abstract class Number-PhoneCard extends PhoneCard 23: 24: long cardNumber; 25: int password; 26: String connectNumber; 27: boolean connected;
17、28:,Java的继承,22,域的隐藏举例,TestHiddenField.java,29: boolean performConnection(long cn,int pw) 30: 31: if(cn = cardNumber ,Java的继承,23,域的隐藏举例,TestHiddenField.java,44: 45: boolean performDial( ) 46: 47: if( balance (0.5 + additoryFee ) 48: 49: balance -= (0.5 + additoryFee); 50: return true; 51: 52: else 53
18、: return false; 54: 55: ,Java的继承,24,域的隐藏举例,TestHiddenField.java显示结果,Java的继承,25,域的隐藏举例,TestHiddenField.java代码说明 第5句创建了一个D200-Card类的对象my200,这个对象有两个balance变量,一个继承自父类PhoneCard,另一个是在第43句中重新定义的自身的balance变量 第6句为my200对象的balance变量赋值,根据域隐藏的原则,这里是为my200自身的balance变量赋值 第7句输出my200对象的getBalance( )方法的返回值,这里的getBala
19、nce( )方法是在父类PhoneCard中定义的,它返回的是my200对象继承自父类PhoneCard的balance变量的数值,这个balance没有被赋值,其数值是缺省的0.0 第8句调用my200对象的performDial( )方法拨打电话,修改my200对象自身的balance变量。第9句输出拨打电话之后,my200对象的balance变量的数值,Java的继承,26,方法的继承,父类的非私有方法可以被子类所继承,各种电话卡所包含方法如下 PhoneCard类:abstract boolean performDial( );double getBalance( )None-Numb
20、er-PhoneCard类:abstract boolean performDial( );/继承自父类PhoneCarddouble getBalance( ) /继承自父类PhoneCardString getSetType( )Number-PhoneCard类:abstract boolean performDial( ); /继承自父类PhoneCarddouble getBalance( ) /继承自父类PhoneCardboolean performConnection(long cn,int pw)magCard类:double getBalance( ) /继承自父类None
21、-Number-PhoneCardString getSetType( ) /继承自父类None-Number-PhoneCardboolean performDial( ),Java的继承,27,方法的继承,根据方法的继承关系,各种电话卡所包含方法如下IC-Card类:double getBalance( ) /继承自父类None-Number-PhoneCardString getSetType( ) /继承自父类None-Number-PhoneCardboolean performDial( )IP-Card类:boolean performDial( )double getBalan
22、ce( ) /继承自父类Number-PhoneCardboolean performConnection(long cn,int pw) D200-Card类:boolean performDial( )double getBalance( ) /继承自父类Number-PhoneCardboolean performConnection(long cn,int pw),Java的继承,28,this与super,this和super是常用来指代子类对象和父类对象的关键字 Java系统默认,每个类都缺省地具有null,this和super三个域,所以在任意类中都可以不加说明而直接使用它们 其
23、中null代表“空”, 代表一个什么也没有的“空”值 在定义一个对象但尚未为其开辟内存单元时可以指定这个对象为null this和super两个域则与继承有密切关系,Java的继承,29,this,在方法及构造方法中,使用this来访问域及方法 this表示当前对象的一个引用 对象的引用可以理解为对象的另一个名字 利用this可以调用当前对象的方法或使用当前对象的域,如double getBalance( )return this.balance; 表示返回的是当前同一个对象的balance域,当然在这种情况下this也可以不加 更多的情况下,this用来把当前对象的引用作为参数传递给其他的对
24、象或方法,Java的继承,30,this,使用this还可以解决局部变量(方法中的变量)或参数变量与域变量同名的问题。如,在构造方法中,经常这样用:Person( int age, String name )this.age = age;this.name = name; 这里,this.age表示域变量,而age表示的是参数变量,Java的继承,31,this,构造方法中还可以用this来调用另一构造方法,如Person( )this( 0, “ ); 如果在构造方法中调用另一构造方法,则这条调用语句必须放在第一句,Java的继承,32,This举例: getDouble.java,1:pa
25、ckage p1; 2: import java.applet.*; 3: import java.awt.*; 4: import java.awt.event.*; 5: public class getDouble extends Applet implements ActionListener 6: 7: Label prompt; 8: TextField input; 9: double d = 0.0; 10: 11: public void init( ) 12: 13: prompt = new Label(请输入一个浮点数:); 14: input = new TextFi
26、eld(10); 15: add(prompt); 16: add(input); 17: input.addActionListener(this); 18: ,Java的继承,33,this,getDouble.java,19: public void paint(Graphics g) 20: 21: g.drawString(你输入了数据: + d, 10, 50); 22: 23: public void actionPerformed(ActionEvent e) 24: 25: d = Double.valueOf(input.getText( ).doubleValue( );
27、 26: repaint( ); 27: 28: ,Java的继承,34,this,getDouble.java说明 第17句调用的addActionListener( )方法是系统类TextField的方法,调用这个方法要求提供一个实现了ActionListener接口的对象作为实际参数 第5句中定义的用户类getDouble利用implements关键字(接口及其实现将在本章后面介绍)实现了ActionListener接口,就使用this将当前getDouble类的对象指定为调用addActionListener( )方法的实际参数,Java的继承,35,super,super表示当前对象
28、的直接父类对象,是当前对象的直接父类对象的引用 所谓直接父类是相对于当前对象的其他“祖先”类而言的 例如,假设类A派生出子类B,B类又派生出自己的子类C,则B是C的直接父类,而A是C的祖先类 同理,Number-PhoneCard类是D200-Card类的直接父类,PhoneCard类是D200-Card类的祖先类 子类自动地继承父类的(非private)属性和方法 一般情况下,使用this可以访问父类的域和方法 有时为了明确地指明父类的域和方法,就要用关键字super,Java的继承,36,super,例如:父类Person有一个域age,在子类Student中用age, this.age,
29、 super.age来访问age是完全一样的void testThisSuper()int a1,a2,a3;a1 = age;a2 = this.age;a3 = super.age;,Java的继承,37,super,有时需要使用super以区别同名的域与方法 使用super可以访问被子类所隐藏了的同名变量 当覆盖父类的同名方法的同时,又要调用父类的方法,就必须使用super 例如 void sayHello()super.sayHello();System.out.println( “My school is “ + school ); 从这里可以看出,即使同名,也仍然可以使用父类的域和
30、方法,这也使得在覆盖父类的方法的同时,又利用已定义好的父类的方法,Java的继承,38,super,在严格意义上,构造方法是不能继承的 比如,父类Person有一个构造方法Person(String, int),不能说子类Student也自动有一个构造方法Student(String, int) 但是,这不并意味着子类不能调用父类的构造方法 子类在构造方法中,可以用super来调用父类的构造方法Student(String name, int age, String school )super( name, age);this.school = school; 使用时,super()必须放在第
31、一句,Java的继承,39,super,TestSuper.java,package p3; 12: abstract class PhoneCard 13: 14: double balance; 16: abstract boolean performDial( ); 17: double getBalance( ) 18: 19: return balance; 20: 21: 22: abstract class Number-PhoneCard extends PhoneCard 23: 24: long cardNumber; 25: int password; 39: ,Java
32、的继承,40,super,TestSuper.java,40: class D200-Card extends Number-PhoneCard 41: 42: double additoryFee; 43: double balance; 44: 45: boolean performDial( ) 46: 47: if( balance (0.5 + additoryFee ) 48: 49: balance -= (0.5 + additoryFee); 50: return true; 51: 52: else 53: return false; 54: 55: double getB
33、alance( ) 56: 57: return super.balance; 58: 59: ,Java的继承,41,super,TestSuper.java,1: public class TestSuper 2: 3: public static void main(String args) 4: 5: D200-Card my200 = new D200-Card( ); 6: my200.balance = 50.0; 7: System.out.println(父类被隐藏的金额为: +my200.getBalance( ); 8:if(my200.performDial( ) 9:
34、 System.out.println(子类的剩余金额为:+my200.balance); 10: 11: ,Java的继承,42,super,TestSuper.java显示结果,Java的继承,43,super,TestSuper.java代码说明 第57句D200-Card类的方法getBalance( )返回的是当前对象的super域的balance变量 当前对象的super域是D200-Card类的直接父类Number-PhoneCard的引用 Number-PhoneCard的balance变量是从PhoneCard类那里继承来的 因此,即使调用子类的getBalance( )方法
35、,返回的仍是没有赋值的父类的balance变量,Java的继承,44,super,TestSuper.java代码说明 注意:this和super是属于类的有特指的域,只能用来代表当前对象和当前对象的直接父对象,而不能像其他类的属性一样随意引用 下面语句中的用法都是错误的D200-Card my200 = new D200-Card( ) ;my200.this.getBalance ( ) ; / 错误my200.super.getBalance ( ) ; / 错误 除了用来指代当前对象或父类对象的引用外,this和super还有一个重要的用法,就是调用当前对象或父类对象的构造方法,Jav
36、a的继承,45,this与super在构造方法中的使用小结,this调用本类的其他构造方法 super调用直接父类的构造方法 如果没有this及super,则编译器自动加上super(),即调用直接父类不带参数的构造方法 this或super要放在第一条语句,且只能够有一条 举例:ConstructCallThisAndSuper.java,Java的继承,46,this与super小结,ConstructCallThisAndSuper.java,package p4; public class ConstructCallThisAndSuper public static void mai
37、n(String args) Person p = new Graduate(); class Person String name; int age;Person()Person( String name, int age )this.name=name; this.age=age; System.out.println(“In Person(String,int)“); ,Java的继承,47,this与super小结,ConstructCallThisAndSuper.java,class Student extends PersonString school;Student( Stri
38、ng name, int age, String school )super(name, age);this.school = school;System.out.println(“In Student(String,int,String)“);Student()this(null, 0, null);System.out.println(“In Student()“); class Graduate extends StudentGraduate()System.out.println(“In Graduate()“); ,Java的继承,48,this与super小结,ConstructC
39、allThisAndSuper.java运行结果 在构造方法中调用this及super或自动加入的super,最终保证了任何一个构造方法都要调用父类的构造方法,而父类的构造方法又会再调用其父类的构造方法,直到最顶层的Object类 这是符合面向对象的概念的,因为必须令所有父类的构造方法都得到调用,否则整个对象的构建就可能不正确,Java的继承,49,父类对象与子类对象的转换,类似于基本数据类型数据之间的强制类型转换,存在继承关系的父类对象和子类对象之间也可以在一定条件下相互转换 父类对象和子类对象的转化需要注意如下原则 子类对象可以被视为是其父类的一个对象 父类对象不能被当作是其某一个子类的对
40、象 如果一个方法的形式参数定义的是父类对象,那么调用这个方法时,可以使用子类对象作为实际参数 如果父类对象引用指向的实际是一个子类对象(在以前的某个时候根据(1)把子类对象的引用赋值给这个父类对象的引用),那么这个父类对象的引用可以用强制类型转换转化成子类对象的引用,Java的继承,50,父类对象与子类对象的转换,举例,/定义父类 class SuperClassint x;/定义子类 class SubClass extends SuperClassint y;char ch; ,/使用父类与子类 public class UseSuperSubSuperClass sc, sc_ref;S
41、ubClass sb, sb_ref;sc = new SuperClass( );sb = new SubClass( );/父类引用可以指向子类对象sc_ref = sb; /父类引用转换成子类引用sb_ref = (SubClass)sc-ref;,Java的继承,51,上溯造型,把派生类型当作基本类型处理只需写单一的代码,只与基础类打交道,忽略派生类型的特定细节。这样的代码易于编写和理解,且若通过继承增添了一种新类型,新类型会像在原来的类型里一样正常的工作,是程序具备了扩展性。doStuff(Shape s)s.erase();/s.draw(); Circle c=new Circl
42、e(); Triangle t= new Triangle(); Line l= new Line(); doStuff(c); doStuff(t); doStuff(l);,Java的继承,52,主要内容,Java的继承 Java的多态 Java的覆盖 Java 的重载 构造方法的重载与继承 内部类与匿名类 接口,53,多态概念,面向过程的语言编程,过程和方法各自对应一定的功能,它们之间是不能重名的,否则在用名字调用时,就会产生歧异和错误 在面向对象的程序设计中,却需要利用这样的“重名”现象来提高程序的抽象度和简洁性,Java的多态,54,多态概念,举例:“拨打电话”是所有电话卡都具有的操
43、作,但是不同的电话卡“拨打电话”操作的具体实现是不同的 磁卡的“拨打电话”是“找到磁卡电话机直接拨号” 200卡的“拨打电话”是“找到双音频电话机,先输入卡号、密码后再拨号” 如果不允许这些目标和最终功能相同的程序用同样的名字,就必须分别定义“磁卡拨打电话”、“200卡拨打电话”等多个方法 这样一来,继承的优势就荡然无存了 在面向对象的程序设计中,为了解决这个问题,引入了多态的概念,Java的多态,55,多态概念,多态 (Polymorphism) 是指一个程序中同名的不同方法共存的情况,即一个程序中相同名字表示不同含义的情况 覆盖(override) (子类对父类方法) 重载(overloa
44、d) (同一个类中定义多个同名的不同方法) 动态绑定(dynamic binding) -虚方法调用(virtual method invoking) 多态优点 提高了程序的抽象程度和简洁性,最大限度地降低了类和程序模块之间的耦合性 提高了类模块的封闭性,使得它们不需了解对方的具体细节,就可以很好地共同工作,Java的多态,56,基于覆盖的多态方法实现,子类对继承自父类的方法的重新定义,就称为方法的覆盖(overriding),是一种很重要的多态的形式 PhoneCard类的每一个子类都将继承这个方法performDial( ) 但该方法代表的相同功能在不同种类的电话卡中的具体实现不同 为了体
45、现这一点,不同的子类可以重新定义、编写“拨打电话”这个方法(performDial( )的内容,以满足本类的特殊需要,实现这个具体电话卡的特定拨打电话的方法 所有子类中,凡是实现拨打电话这种功能的方法,虽然内容不同,但却共享相同的名字“performDial”,Java的多态,57,基于覆盖的多态方法实现,在覆盖多态中,由于同名的不同方法是存在于不同的类(如磁卡、IP卡、200卡等)中的,所以只需在调用方法时指明调用的是哪个类的方法,就可以很容易地把它们区分开来,如 MymagCard.performDial( )(MymagCard是magCard类的对象) my200.performDia
46、l( ),Java的多态,58,基于覆盖的多态方法实现,另外还有一种不明确指定具体子类而把区分工作留给系统的情况 比如设某业务员需要向总部汇报一条消息,总部指示他:用手机或电话卡拨打电话 至于业务员使用何种电话卡,具体如何拨打电话这些细节则不需要总部了解,系统会自动分辨所使用的电话卡是哪种类型的电话卡,并调用相应的拨打电话的具体方法,Java的多态,59,基于重载的多态方法实现,同一类中定义同名方法的情况称为重载(overloading) 方法同名的原因,是因为它们的最终功能和目的都相同,但是由于在完成同一功能时,可能遇到不同的具体情况,所以需要定义含不同的具体内容的方法,来代表多种具体实现形式 例如,一个类需要具有打印的功能,而打印是一个很广泛的概念,对应的具体情况和操作有多种 如实数打印、整数打印、字符打印、分行打印等 为了使打印功能完整,在这个类中就可以定义若干个名字都叫print的方法,每个方法用来完成一种不同于其他方法的具体打印操作,处理一种具体的打印情况,Java的多态,60,基于重载的多态方法实现,由于重载发生在同一个类里,不能再用类名来区分不同的方法,所以一般采用不同的形式参数列表,包括形式参数的个数、类型、顺序的不同,来区分重载的方法 打印实数的print方法的形式参数是一个实数和打印格式串, 打印整数的print方法的形式参数是一个整数和打印格式串,