1、第四章 类的深入解析,4.1 掌握类继承的方法 4.2 掌握类的多态性的使用方法 4.3 了解抽象类的使用方法 4.4 了解接口的定义,4.1 掌握类继承的方法,在面向对象程序设计中,继承表示两个类之间的一种关系,是一种由已有类创建新类的机制。子类不仅可以从父类中继承成员变量和方法,还可以重新定义它们以及扩充新的内容。,在Java中,子类对父类的继承是在类的声明中使用extends关键字来指明的。其一般格式为:类的修饰符class extends /类体的内容,使用继承方法创建新类时,新定义的子类可以从父类继承所有非私有的成员变量和方法作为自己的成员。,一、成员变量的继承与隐藏,实例4-1 成
2、员变量的继承与隐藏示例,【实例描述】通过创建父类和子类,并在其中声明若干同名和不同名成员变量演示成员变量继承和隐藏的特点。 【技术要点】基于父类创建子类时,子类可以继承父类的成员变量和成员方法。但是,如果在父类和子类中同时声明了一个同名变量,则这两个变量在程序运行时同时存在。也就是说,子类在使用父类的同名变量时,父类中的同名变量只是被隐藏了。,package Chapter4; class Person String name; int age; / 声明两个成员变量 public Person(String name, int age) / 有参构造方法this.name = name; t
3、his.age = age; public Person() / 无参构造方法this.name = “person name“; this.age = 23; void pprint() / 成员方法,此时显示的是父类中成员变量的结果System.out.println(“class:Person; “ + “Name: “ + this.name + “; age: “+ this.age); class Student extends Person / 基于Person类定义Student子类String name; / 在派生类中声明自己的成员变量int classno; / 声明新成
4、员变量public Student() / 无参构造方法this.name = “student name“; this.age = 20; public Student(String name, int age, int classno) / 有参构造方法this.name = name; this.age = age; this.classno = classno; void sprint() / 成员方法,此时显示的是子类中成员变量的结果System.out.println(“class:Student; “ + “Name: “ + this.name + “; age: “+ thi
5、s.age + “; classno: “ + this.classno); public class VarInherit / 声明公共类public static void main(String args) Student obj1 = new Student(); / 调用无参构造方法创建对象Student obj2 = new Student(“LiXiao“, 18, 1); / 调用有参构造方法创建对象obj1.pprint(); obj2.pprint(); / 调用父类的成员方法obj1.sprint(); obj2.sprint(); / 调用子类的成员方法 ,解释说明:
6、(1)由于Student子类中未定义成员变量age,因此,该成员变量源自其父类,这说明了子类可以继承父类的成员变量。(2)name变量同时在父类和子类中进行了声明。当我们通过Student obj1 = new Student();语句创建对象obj1时,系统会首先调用父类的无参构造方法,然后再调用子类的无参构造方法,故父类中的name被赋值person name,子类中的name被赋值student name,而公共成员变量age被赋值20。另外,由于子类成员变量classno未被显式赋值,故输出系统自动为其初始化的默认值0。,程序的运行结果: class:Person; Name: per
7、son name; age: 20 class:Student; Name: student name; age: 20; classno: 0 class:Person; Name: person name; age: 18 class:Student; Name: LiXiao; age: 18; classno: 1,二、方法的继承与覆盖,子类可以继承父类中所有可以被子类访问的成员方法,但是如果子类重新定义了从父类继承来的方法,此时父类的这个方法在子类中将不复存在,此时称为子类方法覆盖了父类的方法,简称方法覆盖(override)。,实例4-2 方法的继承与覆盖示例,【实例描述】通过创建
8、父类和子类,并在其中声明若干同名和不同名方法演示方法继承和覆盖的特点。 【技术要点】基于父类创建子类时,子类可以继承父类的成员方法。但是,如果在父类和子类中同时定义了一个同名、同类型、同参数的方法,则程序运行时父类中的同名方法将被子类中的同名方法覆盖。,package Chapter4; class parentclass / 声明父类void pprint() / 声明成员方法this.print(); this.print1(0); void print() / 声明同类型、同名、同参数成员方法System.out.println(“父类:同类型、同名、同参数成员方法!“); void p
9、rint1(int a) / 声明同类型、同名但参数不同的成员方法System.out.println(“父类:同类型、同名但参数不同的成员方法!“); class subclass extends parentclass / 基于parentclass类定义subclass子类void sprint() / 声明成员方法this.print(); this.print1(); void print() / 声明同类型、同名、同参数成员方法System.out.println(“子类:同类型、同名、同参数成员方法!“); void print1() / 声明同类型、同名但参数不同的成员方法Sy
10、stem.out.println(“子类:同类型、同名但参数不同的成员方法!“); public class MethodInherit / 声明公共类public static void main(String args) subclass obj = new subclass();obj.pprint(); / 调用父类的成员方法obj.sprint(); / 调用子类的成员方法 ,解释说明: (1)使用子类创建对象时,可以直接引用父类中的方法,如利用obj.pprint();语句调用父类中的pprint成员方法。这体现了方法的继承性。(2)要使子类中的方法完全覆盖父类中的方法,方法的类型
11、、名称和参数必须完全相同(如两个print方法),否则,任何一项不同均不能覆盖(如两个print1方法)。,程序的运行结果: 子类:同类型、同名、同参数成员方法! 父类:同类型、同名但参数不同的成员方法! 子类:同类型、同名、同参数成员方法! 子类:同类型、同名但参数不同的成员方法!,三、构造方法的继承,当我们基于子类创建一个对象时,系统会首先调用父类的无参构造方法,然后才会执行子类的构造方法。这体现了构造方法的继承性。,实例4-3 构造方法的继承示例,【实例描述】通过创建父类和子类,并分别为其创建若干构造方法演示构造方法的继承特点。 【技术要点】基于父类创建子类时,如果我们基于子类创建了一个
12、对象,则程序运行时,系统会首先调用父类的无参构造方法,然后才会执行子类的构造方法。如果希望调用父类的有参构造方法,可使用super关键字。,package Chapter4; class PersonA String name; int age; / 声明两个成员变量public PersonA(String name, int age) / 有参构造方法this.name = name; this.age = age; public PersonA() / 无参构造方法this.name = “person name“; this.age = 23; void pprint() / 成员方法
13、,此时显示的是父类中成员变量的结果System.out.println(“class:Person; “ + “Name: “ + this.name+ “; age: “ + this.age); class StudentA extends PersonA String name; int classno; public StudentA() / 无参构造方法super(“xyz“, 30); / 调用父类的有参构造方法this.name = “student name“; this.age = 20; public StudentA(String name, int age, int c
14、lassno) / 有参构造方法super(name, age); / 调用父类的有参构造方法this.name = name; this.age = age; this.classno = classno; void sprint() / 成员方法,此时显示的是子类中成员变量的结果System.out.println(“class:Student; “ + “Name: “ + this.name+ “; age: “ + this.age + “; classno: “ + this.classno);/ 成员方法,利用super命令显示父类成员变量System.out.println(“
15、父类中的name成员变量:“ + super.name); public class ConstructorInherit / 声明公共类public static void main(String args) StudentA obj1 = new StudentA(); / 调用无参构造方法创建对象StudentA obj2 = new StudentA(“LiXiao“, 18, 1); / 调用有参构造方法创建对象obj1.pprint(); obj2.pprint(); / 调用父类的成员方法obj1.sprint(); obj2.sprint(); / 调用子类的成员方法 ,构造方
16、法的继承遵循以下原则:子类无条件地继承父类的无参数的构造方法。 如果子类没有定义构造方法,则它将继承父类无参数的构造方法作 为自己的构造方法;如果子类定义了构造方法,则在创建子类对象 时,将先执行来自继承父类的无参数的构造方法,然后再执行自己 的构造方法。 对于父类带参数的构造方法,子类可以通过在自己的构造方法中使 用super关键字来调用它,但这个调用语句必须是子类构造方法中的 第一条可执行语句。,程序的运行结果: class:Person; Name: xyz; age: 20 class:Student; Name: student name; age: 20; classno: 0 父
17、类中的name成员变量:xyz class:Person; Name: LiXiao; age: 18 class:Student; Name: LiXiao; age: 18; classno: 1 父类中的name成员变量:LiXiao,四、使用类继承时子类对象和父类对象的特点,使用类继承时,使用子类创建的对象可以赋值给父类对象。此时父类对象虽然名义上调用的仍是父类方法或成员变量,但实际上已变成了子类的方法和成员变量。,package Chapter4; class Pclass / 创建父类Pclassvoid Draw() System.out.println(“Pclass类,Dra
18、w方法!“); class Sclass extends Pclass / 创建子类Sclassvoid Draw() System.out.println(“Sclass类,Draw方法!“); void NewDraw() System.out.println(“Sclass类,NewDraw方法!“); public class ClassInherit / 创建公共类public static void main(String args) Pclass obj1 = new Pclass(); Sclass obj2 = new Sclass(); obj1.Draw(); / 调用O
19、bj1的Draw方法obj2.Draw(); / 调用obj2的Draw方法obj2.NewDraw(); / 调用obj2的NewDraw方法obj1 = obj2; / 将子类对象赋值给父类对象/ 调用转换后obj1对象的Draw方法(实际上已是子类对象的Draw方法)obj1.Draw(); ,4.2 掌握类的多态性的使用方法,一、多态性的概念,多态性的实现有两种方式。 (1)方法覆盖实现多态性此时通过子类对继承父类的方法进行重定义来实现。 (2)方法重载实现多态性 通过定义多个同名的不同方法来实现,系统会根据参数(类型、个数、顺序)的不同来区分不同方法。,二、通过方法覆盖实现多态性,如
20、果我们基于某个父类创建了多个子类,而在子类中分别重定义了父类的某些方法,那么当我们分别基于父类和子类创建多个对象时,就可以利用这些对象分别调用不同的同名方法。,import java.util.*; class Shape / 创建一个Shape基类public void draw() public void erase() class Circle extends Shape / 基于Shape类创建Circle子类public void draw() System.out.println(“Circle.draw()“); public void erase() System.out.pr
21、intln(“Circle.erase()“); class Square extends Shape /基于Shape类创建Square子类public void draw() System.out.println(“Square.draw()“); public void erase() System.out.println(“Square.erase()“); class Triangle extends Shape /基于Shape类创建Triangle子类public void draw() System.out.println(“Triangle.draw()“);public v
22、oid erase() System.out.println(“Triangle.erase()“); class RandomShapeGenerator / 创建随机形状发生器类RandomShapeGenerator/ 创建Random类型rand随机数发生器,其种子为长整型数47private Random rand = new Random(47);public Shape next() / 创建Shape类型方法nextswitch (rand.nextInt(3) / rand.nextInt(3)用来产生03之间的随机数default:case 0:return new Cir
23、cle(); / 返回Circle对象case 1:return new Square(); / 返回Square对象case 2:return new Triangle(); / 返回Triangle对象 ,public class Shapes / 定义公共类private static RandomShapeGenerator gen = new RandomShapeGenerator();public static void main(String args) Shape s = new Shape9;/ 基于Shape类创建对象数组s/ 为对象数组s赋值,其内容应为Circle、S
24、quare或Triangle子类对象for (int i = 0; i s.length; i+) si = gen.next();for (Shape shp : s) / 遍历数组s,将其值赋给临时Shape对象shp/ 调用shp对象的draw方法。由于shp对象内容分别为Circle、 Square或/ Triangle类型,故draw方法也分别隶属于这三种类型shp.draw(); ,解释说明:基于父类和子类创建对象时,可以将子类对象赋给父类对象,反之则不能。不过,此时父类对象依然只能调用其自身的成员方法(被子类覆盖的方法除外,由于此时只存在子类的覆盖方法,因此,此时父类对象调用的实
25、际是子类对象的覆盖方法),以及访问自身的成员变量。,二、通过重载方法实现多态性,在定义多个重载方法时,除了使它们的方法名相同外,必须采用不同形式的参数,包括参数的个数不同、类型不同或顺序不同。如此一来,调用重载方法时,系统会根据参数的个数、类型或顺序来确定调用哪一个方法。,package Chapter4; public class MethodOverloaded int add(int x1, int x2) / 两个整数相加,返回整数return x1 + x2; double add(double x1, double x2) / 两个浮点数相加,返回浮点数return x1 + x2
26、; public static void main(String args) MethodOverloaded obj = new MethodOverloaded(); / 分别调用不同的重载方法System.out.println(“int add= “ + obj.add(10, 23);System.out.println(“double add= “ + obj.add(10.0, 23); 运行结果:int add= 33 double add= 33.0,4.3 了解抽象类的使用方法,(1)抽象类的定义格式通常如下:public abstract class PlaneGraph
27、cs1 / 声明抽象类private String shape; / 声明成员变量public abstract double area(); / 声明抽象方法,并且分号必不可少(2)抽象类是不能实例化的,也就是说,不能基于抽象类来创建对象。(3)抽象类也可以包含普通成员变量和成员方法。但是,抽象方法只能出现在抽象类中。也就是说,非抽象类是不能包含抽象方法的。 抽象类的示例请参考实例4-5。,抽象类即为类的抽象,是对相似类的归纳与总结。因此,抽象类中通常只包括了抽象方法(只包含方法声明,而不包含方法体),而方法的具体实现则由其派生出的各子类来完成,这使得程序的功能描述和功能实现得以分离。,4.
28、4 了解接口的定义,和抽象类的作用类似,接口也可以把“做什么”和“怎么做”,即程序的功能和实现分离开来。 在Java中,一个类只能直接继承一个父类,但可以同时实现若干个接口。因此,利用接口实际上就获得了多个特殊父类的属性,即实现了多继承。,从某种意义上讲,接口应该属于更严格的抽象类。接口中只能定义抽象方法,而抽象类中可以定义非抽 象方法。 接口中只能定义public static final类型常量,而不能包 含成员变量。抽象类则不然,它可以包含各种形式的 成员变量和常量。类可以继承(实现)多个接口,但只能继承一个抽象 父类。,一、接口的定义,接口的定义包括两个部分:接口声明和接口体。声明接口
29、的一般格式如下:public interface 接口名 extends 父接口名列表/常量声明/抽象方法声明,接口声明中有两个部分是必需的:interface关键字和接口的名字。用public修饰的接口是公共接口,可以被所有的类和接口使用;没有public修饰的接口则只能被同一个包中的其他类和接口使用。接口也具有继承性,子接口可以继承一个或多个父接口的所有属性和方法。继承多个父接口时,各父接口名以逗号分隔。,二、接口的实现,为了声明一个类来实现一个接口,在类的声明中要包括一条implements语句。此外,由于Java支持接口的多继承,因此可以在implements后面列出要实现的多个接口,
30、这些接口名称之间应以逗号分隔。,class Stock implements StockWatcher, FundWatcher public void valueChanged(String tickerSymbol, double newValue) if(tickerSymbol.equals(oracleTicker)else if(tickerSymbol.equals(ciscoTicker) ,注意:由于Stock类实现了StockWatcher接口,因此它应该提供valueChanged()方法的实现。当一个类实现一个接口中的抽象方法时,这个方法的名字和参数类型、数量和顺序必须
31、与接口中的方法相匹配。 接口的示例请参考实例4-6。,本章小结,本章进一步介绍了Java语言中面向对象编程的相关知识,主要包括类的继承和多态的实现方式,以及抽象类和接口的定义与使用方法。大家应着重掌握如下一些内容:,在使用子类创建对象时,可以利用子类对象名直接引用父类中的成员变量和成员方法,这被称为成员变量和方法的继承; 使用子类创建对象时,父类的无参构造方法总是优先被执行,这被称为构造方法的继承。当然,我们也可以利用super关键字显式调用父类的其他构造方法; 多态性被称为“一个对外接口,多个内在实现方法”,它可以通过方法覆盖和重载方法来实现。 抽象类除了可以包含抽象方法外,其他性质与普通类完全相同。抽象类自身不能实例化,抽象方法的实现应通过其派生子类来完成; 接口是更严格的抽象类,其中只能包含public static final成员常量和抽象方法; 一个子类只能继承一个抽象类,但可以继承多个接口,这实现了程序设计中的多继承关系。,