Presentation is loading. Please wait.

Presentation is loading. Please wait.

第六章 类的扩展与继承.

Similar presentations


Presentation on theme: "第六章 类的扩展与继承."— Presentation transcript:

1 第六章 类的扩展与继承

2 教学内容 6.1 使用现有的类 6.2 类的继承 6.3 选择基类的访问属性 6.4 多态 多级继承 6.6 抽象类 通用超类 6.5
6.7

3 教学内容 6.8 接收可变实参的方法 6.9 对象的类型强制转换 6.10 关于枚举 6.11 类的设计 使用final修饰符 6.13
6.12 接口 6.13 匿名类 6.14

4 6.1 使用现有的类 class Spaniel extends Dog{ //Members of the Spaniel class…
6.1 使用现有的类 class Spaniel extends Dog{ //Members of the Spaniel class… } 派生:在一个现有的类基础上定义新的类,新生成的类即为派生类。 原始的类称为基类(超类),派生出的类称为基类的直接子类。 实例: class Dog{ //Members of the Dog class… }

5 6.1 使用现有的类 分析下面类的关系: class A 派生自 B的直接超类 C的间接超类 class B 派生自 A的直接子类
6.1 使用现有的类 分析下面类的关系: class A 派生自 B的直接超类 C的间接超类 class B 派生自 A的直接子类 C的直接超类 class C B的直接子类 A的间接子类

6 6.2 类的继承 类的继承:在派生类中包含其基类的成员,以便它们在派生类中可以被访问的过程称为类的继承。
6.2 类的继承 类的继承:在派生类中包含其基类的成员,以便它们在派生类中可以被访问的过程称为类的继承。 继承成员:可以在其派生类中访问的基类的成员。如果一个基类的成员不能在其派生类中被访问,那么它就不是派生类的继承成员,但是基类的非继承成员仍然形成了派生类对象的一部分。 派生类类型的对象包含 其基类的所有继承成员:成员域和方法 派生类自己的成员。

7 6.2 类的继承 注意: 一个派生类对象总是包含完整的基类对象,包括所有未继承的成员域或方法。
6.2 类的继承 注意: 一个派生类对象总是包含完整的基类对象,包括所有未继承的成员域或方法。 Java规定一个子类只能有一个父类(单重继承) 继承实现了软件的重用。 由extends 完成继承关系的定义

8 6.2.1 继承数据成员 包2 包1 子类2 public MyClass 子类1 public int b;
继承数据成员 子类2 public MyClass 子类1 public int b; protected int c; int a; public int b; protected int c; private int e; int a; public int b; protected int c; No 被继承到 被继承到 被继承到 被继承到 被继承到 No No 包2 包1

9 6.2.1 继承数据成员 说明: 1.类本身声明为public,该类可在任何地方被任何包访问。
继承数据成员 说明: 1.类本身声明为public,该类可在任何地方被任何包访问。 2.与基类相同的包中定义的子类除基类的private数据成员外所有成员。 3.在包含基类的包外定义子类,则基类的private及没有指定访问属性的任何数据成员也不会被继承。

10 属性和方法的访问控制 public protected default private 同类 同包 子类 通用性 访问说明符 不同 情形下

11 属性和方法的访问控制  public protected default private 同类 同包 子类 通用性 访问说明符 不同
情形下

12 属性和方法的访问控制  public protected default private 同类 同包 子类 通用性 访问说明符 不同
情形下

13 属性和方法的访问控制  public protected default private 同类 同包 子类 通用性 访问说明符 不同
情形下

14 属性和方法的访问控制  public protected default private 同类 同包 子类 通用性 访问说明符 不同
情形下

15 6.2.1 继承数据成员 super:指代对父类的引用,实现对父类成员的访问。
继承数据成员 super:指代对父类的引用,实现对父类成员的访问。 子类在隐藏了父类的成员变量或重写了父类的方法后,常常还要用到父类的成员变量,或在重写的方法中使用父类中被重写的方法,这时就要通过super来实现对父类成员的访问。

16 public class Person { private String name; private int age; private Date birthDate; public String getInfo() { return "Name: " + name + "\nage: " + age; } } public class Student extends Person { private String school = "New Oriental"; public String getSchool(){ return school; } return super.getInfo() +"\nschool: " +school; }

17 6.2.1 继承数据成员 访问父类被隐藏的成员变量 如:super.variable
继承数据成员 super的使用有三种情况: 访问父类被隐藏的成员变量 如:super.variable 调用父类中被重载的方法 如:super.Method ([paramlist]); 调用父类的构造方法 如:super ([paramlist]); 注意:不能使用super.super.someting这样的名字去引用基类中的基类的成员名。

18 6.2.2 继承方法 子类的对象 基类 子类 示例: TestDerived Inherited Members public
继承方法 子类的对象 Inherited Members public protected Inaccessible Basic Members New Members subclass constructors subclass data members subclass methods 基类 子类 public protected no attribute private constructors public protected subclass constructors subclass data members subclass methods 示例: TestDerived

19 6.2.3 覆盖基类的方法 派生类中定义的方法可以与基类中某个方法具有相同的签名。
覆盖基类的方法 派生类中定义的方法可以与基类中某个方法具有相同的签名。 派生类中定义的方法的访问属性可与基类中同名方法相同或限制更少。 若派生类中具有与基类同名方法,则派生类对象调用派生类的方法,而不是从基类继承的方法。 示例:TestDerived2.java TestDerived3.java

20 覆盖基类的方法 注意事项: 方法的覆盖将清除父类方法占用的内存,从而使父类方法在子类中不存在—如toString()(与成员变量的隐藏比较) 由于覆盖现象是同名方法分别属于不同的类,所以要用不同的类名或对象名调用 在子类中重新定义方法时,要保持与父类完全相同的方法名、返回值和参数列表 子类不能覆盖父类的final方法;也不能覆盖父类的static方法,但可以隐藏父类的static方法 非抽象子类必须覆盖父类的抽象方法。

21 思考:关于内部类、超类、外部类 The way to access each level of classes:
inner class: this.X super class: super.X enclosing class: EnclosingClassName.this.X

22 思考:关于内部类、超类、外部类 class SuperClass { //String s = "super"; }
public class ScopeConflict { String s = "outer"; class Inner extends SuperClass { //String s = "inner"; void foo(){ System.out.println(s); }

23 思考:关于内部类、超类、外部类 Test in another class: ScopeConflict sc = new
ScopeConflict.Inner inner = sc.new Inner(); inner.foo(); output: outer

24 思考:关于内部类、超类、外部类 class SuperClass { String s = "super"; }
public class ScopeConflict { String s = "outer"; class Inner extends SuperClass { //String s = "inner"; void foo(){ System.out.println(s); }

25 思考:关于内部类、超类、外部类 Test in another class: ScopeConflict sc = new
ScopeConflict.Inner inner = sc.new Inner(); inner.foo(); output: super Super class has scope between Enclosing and Inner class

26 思考:子类对象与父类对象的创建 子类对象的初始化要使用到父类的构造函数。 子类对象初始化 父类 构造函数

27 思考:子类对象与父类对象的创建 class SuperClass { 在子类中只实例化了一个子类对象。 }} 从输出结果上看:
System.out.println("SuperClass constructor"); }} 思考:子类对象与父类对象的创建 在子类中只实例化了一个子类对象。 从输出结果上看: 程序并不是一开始就运行自己的构造方法, 而是先运行其父类的默认构造方法。 注意:程序自动调用其父类的默认构造方法。 public class SubClass extends SuperClass { SubClass() { System.out.println("SubClass constructor"); } public static void main(String[] args) { SubClass sub = new SubClass();

28 class SuperClass{ SuperClass(String str) { System. out
class SuperClass{ SuperClass(String str) { System.out.println("Super with a string."); } 思考:子类对象与父类对象的创建 SuperClass() { }; 方法1:在父类中增加一个默认构造方法。 SuperClass(){}; //SuperClass() { }; super(str); 方法2:在子类的构造方法中第一句增加“super(str)” SubClass未找到其父类的默认构造方法SuperClass() ,编译通不过 public class SubClass extends SuperClass{ SubClass(String str) { System.out.println("Sub with a string.");} public static void main(String[] args) { SubClass sub = new SubClass("sub"); }

29 6.3 选择基类的访问属性 构成一个类的外部接口的方法应该被声明为public。一般不要把数据成员声明为public。
6.3 选择基类的访问属性 构成一个类的外部接口的方法应该被声明为public。一般不要把数据成员声明为public。 数据成员可声明为private。 被相同包的其他类访问,但阻止其他包中的类直接访问(其他包中该类的子类可以访问),则可将基类成员声明为protected。 在相同包中由其他类直接访问,但阻止该包外的子类的继承,则可将基类成员的声明缺省,即为package。

30 6.4 多态 多态:是指一个程序中同名的不同方法共存的情况。面向对象的程序中多态的情况有两种:
6.4 多态 多态:是指一个程序中同名的不同方法共存的情况。面向对象的程序中多态的情况有两种: 动态多态:通过子类对父类方法的覆盖(override),有时也叫方法重写。 静态多态:利用重载(overload) 在同一个类中定义多个同名的不同方法。 动态多态:一个特定类型的变量可以用于引用不同类型的对象,并且自动调用该变量引用的特定类型对象的方法。

31 6.4 多态 class Student { public //… float calcTuition() { //… } } class GraduateStudent extends Student void fn(Student x) { x.calcTuition(); } void main() { Student s; GraduateStudent gs; fn(s); fn(gs); }

32 6.4 多态

33 6.4 多态 class Shape{ System.out.println(“shape”); }
6.4 多态 class Shape{ void draw( ){ System.out.println(“shape”); } class Circle extends Shape{ System.out.println(“circle”);

34 6.4 多态 class Rectangle extends Shape{ void draw( ){ System.out.println(“rectangle”); } public class TestPolymorphism { public static void main( String[ ] args ) { Shape s = new Circle( ); s.draw( ); Shape r= new Rectangle( ); r.draw( );

35 6.4 多态 关于多态的几点说明: 派生类的对象的引用可存储在该派生类类型的变量中,也可存储在任何直接或间接的基类类型的变量中。
6.4 多态 关于多态的几点说明: 派生类的对象的引用可存储在该派生类类型的变量中,也可存储在任何直接或间接的基类类型的变量中。 多态根据包含在一个方法调用中的对象的实际类型来决定应该调用哪个方法,而不是由用来存储对象引用的变量的类型决定。 派生类的方法返回对象类型必须与基类中对应方法的相同,或是该基类类型的子类的类型。 当返回对象类型不同,但派生类的方法返回对象类型是基类的返回对象类型的子集时,返回类型称为协变。 示例程序:TryPolymophism

36 6.4 多态 使用多态时需要满足的条件: 派生类对象的方法调用必须通过一个基类类型的变量进行。 调用的方法必须在派生类中被定义。
6.4 多态 使用多态时需要满足的条件: 派生类对象的方法调用必须通过一个基类类型的变量进行。 调用的方法必须在派生类中被定义。 调用的方法也必须被声明为基类的一个成员。 基类和派生类中对应方法的签名必须相同。 基类和派生类的方法的返回对象类型必须相同或者返回对象类型必须是协变的。 派生类的方法的访问说明符不能比基类有更多的限制。

37 6.5 多级继承 一个基类A可派生出派生类B,该派生类又可派生出派生类C,则基类A是派生类B的直接超类,是派生类C的间接超类。
6.5 多级继承 一个基类A可派生出派生类B,该派生类又可派生出派生类C,则基类A是派生类B的直接超类,是派生类C的间接超类。 示例程序:TryPolymophism2

38 6.6 抽象类 抽象类(abstract class):是指声明一个或多个方法但是没有定义这些方法的类。
6.6 抽象类 抽象类(abstract class):是指声明一个或多个方法但是没有定义这些方法的类。 抽象方法(abstract method):没有定义的方法(即方法的主体被省略)称为抽象方法。 抽象方法的声明:以分号结束,用关键字abstract进行标识。 例如: public abstract class … 或 abstract public class…… 一个abstract方法不能声明为private,因为private方法不能被继承,因此不能在子类中进行重新定义。

39 6.6 抽象类 public abstract class Animal{ public abstract void sound(); public Animal (String aType){ type=new String(aType); } public String toString(){ return “This is a ”+type;} private String type;

40 6.7 通用超类 Object是每个类的超类,一个Object类型的变量可以存储任何类的对象的引用。 Object的7个public方法:
6.7 通用超类 Object是每个类的超类,一个Object类型的变量可以存储任何类的对象的引用。 Object的7个public方法: toString() equals() getClass() hashCode() notify() notifyAll() wait() 2个protected方法: clone() finalize()

41 6.7.1 toString方法 作用:获得一个对象的String表示形式。 返回值:类名@对象的十六进制散列码

42 6.7.2 确定对象的类型 getClass() 方法的作用:返回一个Class类型的对象,它会识别一个对象的类。
确定对象的类型 getClass() 方法的作用:返回一个Class类型的对象,它会识别一个对象的类。 getName()方法的作用:是Class类的一个成员,以String对象返回一个完整的类名(在默认的无名包),或是以包名作前缀的类名。 例:Class objectType=pet.getClass(); System.out.println(objectType.getName()); 或 System.out.println(pet.getClass().getName());

43 6.7.2 确定对象的类型 Class类:Class类的对象主要是JVM使用,而且它没有公共的构造函数,用户不能生成自己的Class类对象。
确定对象的类型 Class类:Class类的对象主要是JVM使用,而且它没有公共的构造函数,用户不能生成自己的Class类对象。 若用户添加.class作为任何类、接口、或是基本类型的后缀,即拥有了该类的Class对象的引用。 例: if(pet.getClass()==Duck.class){ System.out.println(“By George-it is a duck!”); }

44 对象的复制 Clone()方法的作用:当要复制的对象允许被复制时(即相应的类必须实现Cloneable接口)生成一个与当前对象类型相同的新的对象,且新对象的每个成员域都设置成与当前对象的对应成员域相同的值。 提示:当原始对象的数据成员引用一个类对象时,这个类对象在克隆时并不被复制,这意味着克隆对象和原始对象都可以通过它们对应的数据成员修改所引用的共享对象。 示例程序:TestCloning TestCloning2

45 6.8 接收可变实参的方法 编写的方法在被调用时可以接收任意数量的实参,且传递的实参不需要具有相同的类型。 Object …args
6.8 接收可变实参的方法 编写的方法在被调用时可以接收任意数量的实参,且传递的实参不需要具有相同的类型。 Object …args 类型名和数组名之间的三个点能使编译器判断出实参表可变。 args表示一个Object[]类型的数组,并且实参值在Object类型的数组元素中可用。args数组的长度提供了接收实参的个数。 示例程序:TryVariableArgumentList TryLimitedVariableArgumentList

46 6.9 对象的类型强制转换 在把一个对象的类型强制转换为另一种类型时,只能在当前对象类型和新的类型位于相同派生类层次结构中的时候进行,并且其中一个是另一个的超类。 通过直接或间接地超类的类型,可以把一个对象的引用由下向上进行强制转换。 若要在两个不相关的对象间进行类型强制转换操作,可通过编写构造函数来进行转换。

47 6.9 对象的类型强制转换 Spaniel aPet=new Spaniel(“Fang”);
6.9 对象的类型强制转换 Spaniel aPet=new Spaniel(“Fang”); Animal theAnimal=(Animal)aPet; 或 Animal theAnimal=aPet; 类Object 派生自 类Animal 派生自 派生自 派生自 类Cat 类Dog 类Duck 派生自 类Spaniel

48 6.9.1 何时强制转换对象的类型 向上强制类型转换:
何时强制转换对象的类型 public class Duck extends Animal{ public void layEgg(){ System.out.println(“Egg laid”); } // Rest of the class as before.. } 向上强制类型转换: 执行多态方法时,对象存储在基类类型变量中,而要在其派生类中调用这些方法,则要把派生类的对象强制转换为基类的对象。 把可能拥有多个子类的对象传递给方法。 public class LayEggs{ public static void main(String[] args){ Duck aDuck=new Duck(“Donald”,”Eider”); Animal aPet=aDuck; aPet.layEgg(); //此句不能正常编译 } ((Duck)aPet).layEgg();

49 6.9.2 对象的识别 instanceof操作符: 与getClass()方法的区别:
对象的识别 instanceof操作符: 检查左操作数引用的对象强制转换为右操作数指定类型的操作是否合法。 若左侧对象的类型与右侧操作数的类型相同或者属于它的任何子类,则结果为true。 与getClass()方法的区别: getClass()方法是获取对象变量所引用对象的类型,用该类型进行左右匹配时要用==进行比较,且要求左右两侧类型必须完全相同。

50 if (pet instanceof Dog) { System. out. println(“You have a dog
if (pet instanceof Dog) { System.out.println(“You have a dog!”); }else { System.out.println(“It’s definitely not a dog!”); } 假设pet存储Spaniel类型对象的引用 if (pet.getClass()==Dog.class) { System.out.println(“You have a dog!”); } else { System.out.println(“It’s definitely not a dog!”); }

51 6.10 关于枚举 定义枚举类型时,类型中的枚举变量作为同一个类的实例生成。该类以java.lang包中的Enum类作为超类。
关于枚举 定义枚举类型时,类型中的枚举变量作为同一个类的实例生成。该类以java.lang包中的Enum类作为超类。 枚举常量的对象: 在枚举类型的成员域中存储 也存储一个整数成员域,从0开始作为第一个常量 继承了Enum类的toString方法 枚举类型的定义: 放在类的定义中 作为一个单独的源文件

52 关于枚举 有以下枚举类型定义 enum Season {spring,summer,fall,winter} 程序片断如下: Season now=Season.winter; if (now.equals(Season.winter)) System.out.println(“It is definitely winter!”); 或 if (now.compareTo(Season.summer)>0) System.out.println(“It is definitely getting colder!”); 示例程序:TryEnumeration

53 类的设计 课后自读内容,程序部分上机调试

54 6.12 使用final修饰符 作用: 注意:抽象类不能声明为final,因为它一定会在子类的某个地方被再定义。
固定类的静态数据成员的值。 例如:final int FEET_PER_YARD=3; 若方法声明为final,则阻止一个子类覆盖自己类中的方法,该方法不能重新定义。 若类声明为final,则该类不能派生出任何子类。 注意:抽象类不能声明为final,因为它一定会在子类的某个地方被再定义。

55 6.13 接口 作用: 指定一组方法来表示一个特定的类接口,而这些方法能在很多不同的类中正确地实现。
6.13 接口 作用: 指定一组方法来表示一个特定的类接口,而这些方法能在很多不同的类中正确地实现。 所有类都可以共享这个通用的接口,且可使用接口类型的变量来多态调用其中的方法。 什么是接口:是一组相关常量和(或)抽象方法,在大多数情况下,接口只包含方法。 接口中的方法:只定义方法的形式 名称 参数 返回对象类型

56 6.13 接口 接口1 接口2 接口3 常量 常量和方法 方法 接口中的常量在默认状态下总是public、static、final类型的
6.13 接口 接口1 接口2 接口3 常量 常量和方法 方法 接口中的常量在默认状态下总是public、static、final类型的 接口中的方法在默认状态下总是public和abstract类型的

57 6.13.1 封装程序中的常量 若在程序中使用的一组常量值只希望定义一次,处理方法如下:
封装程序中的常量 若在程序中使用的一组常量值只希望定义一次,处理方法如下: 在接口中定义一组相关常量,然后在任何使用这些常量的类中实现该接口。 导入类的静态成员。 接口中的常量 public interface ConversionFactors{ double POUND_TO_GRAM= ; double HP_TO_WATT=745.7; double WATT_TO_HP=1.0/HP_TO_WATT; }

58 封装程序中的常量 public class MyClass{ public static double poundsToGrams(double pounds){ return pounds*ConversionFactors.POUND_TO_GRAM; } //plus the rest of the class definition…. } public class MyOtherClass implements ConversionFactors{ public static double poundsToGrams(double pounds){ return pounds*POUND_TO_GRAM; } // plus the rest of the class definition…. }

59 封装程序中的常量 类中定义的常量 package conversions; public class ConversionFactors{ public static final double POUND_TO_GRAM= ; public static final double HP_TO_WATT=745.7; } 常量的使用: 使用数据成员的限定名,如ConversionFactors.HP_TO_WATT 将该类的静态成员导入到需要使用这些常量的类中 示例程序:TryConversions

60 6.13.2 声明方法的接口 接口的主要作用是为了定义表示特定功能的一组方法的外部形式。
声明方法的接口 接口的主要作用是为了定义表示特定功能的一组方法的外部形式。 接口中的方法是public类型,因此在类中定义这些方法时必须使用public。 一个类可以实现多个接口,多个接口间用逗号分隔。如 public class myClass implements Conversions, Definitions, Detections{ //……….. } 示例程序:TryConversions2

61 6.13.3 接口的扩展 接口的扩展:使用extends在一个接口基础上定义另一个接口。
接口的扩展 接口的扩展:使用extends在一个接口基础上定义另一个接口。 优点:扩展后的接口从其原始接口得到所有方法和常量,不需要包含常量的特定类,也没有必要使用静态导入语句。 多重继承: public interface MyInterface extends HisInterface, HerInterface { //……… } 在Java语言中,类不支持多重继承,而接口支持。

62 接口的应用 接口和多态: 用户不能生成一个接口类型的对象,但可生成一个接口类型的变量。 该变量可存储实现接口的任何的类型的对象的引用。 该变量可多态调用在接口中声明的方法。 例:在家中有一台电视,一台高保真录音机,一台录像机,还有一个DVD,每个设备都有它自己的遥控器。所有遥控器都会有一些公共的按钮子集,就会考虑一个遥控器来代替这些遥控器。

63 6.13.5 接口类型的方法形参 用户可以把方法的形参指定为接口类型。 任何类型对象的引用都能够作为实参被传递,只要对象类型实现了接口。
接口类型的方法形参 用户可以把方法的形参指定为接口类型。 任何类型对象的引用都能够作为实参被传递,只要对象类型实现了接口。 通过将形参指定为接口类型,暗示着该方法只在接口的方法中可用。只要一个对象具有实现那些方法的类型它就可以作为实参使用。

64 6.13.6 接口定义中的嵌套类 内部类:将类的定义放在接口的定义中。一个接口的内部类在默认情况下是static和public类型。
接口定义中的嵌套类 内部类:将类的定义放在接口的定义中。一个接口的内部类在默认情况下是static和public类型。 内部类结构: interface Port { //Methods & Constants declared in the interface…. class Info { //Definition of the class…. }} 内部类对象的生成:Port.Info info=new Port.Info();

65 匿名类 匿名类的用途:仅为了在程序中定义一个对象而定义类,且这个对象的唯一作用就是将它作为实参传递给方法。只要类扩展已存在的类或实现接口,就可选择将其定义为匿名类。 匿名类出现在新表达式中,在其中生成并使用类的对象,因此没有必要提供类名。 pickButton.addActionListener(new ActionListener(){ // code to define the class //that implemetns the ActionListener interface });

66 Thank you!

67 小结 本章主要学习了以下内容: 如何在已有类的基础上定义一个新的类以实现类的重用 什么是多态,如何利用多态来定义类 什么是抽象方法
什么是抽象类 什么是接口以及如何定义自己的接口 如何在类中使用接口 如何使用接口实现多态类


Download ppt "第六章 类的扩展与继承."

Similar presentations


Ads by Google