第6章 继承和多态 伍孝金 Email:wxj@jcut.edu.cn
第6章 继承和多态 授 课 大 纲 6.1 继承 6.2 多态 6.3 访问权限修饰符
6.1 继承:父类和子类 什么是继承? 继承是Java语言对客观世界的反映,也是Java语言三大特征之一。 生物世界:人类->爷爷、父亲、儿子 文化传承:中国5000年的历史文化的发展 飞行世界:飞行器->飞机、火箭、飞船 几何图形:几何图形->圆、矩形和三角形 何为继承?从已经存在的类中定义新的类。 继承的特点:相同的属性和方法,继承与发展,这后面的更能反映继承的特性。
6.1 继承:父类和子类 什么继承 父亲从爷爷那里继承了相貌特征,儿子又从父亲那里继承了相貌特征 圆、矩形和三角形都继承于几何图形 为了准确表达这种继承的层次关系,提出了父类和子类的概念:如一个类继承于另一个类,那么这个类称为子类(child class),另一个类就称为父类(parent class)。父类也称为超类(superclass) 、基类(base class),子类也称为次类(subclass)、扩展类(extended class)或派生类(derived class)。
6.1 继承:父类和子类
6.1 继承 如何表示继承关系? Java语言中,使用extends表示子类继承父类,这种继承关系的语法如下: class 子类 extends 父类{ //属性和方法 } 比如:假如定义了一个父类Geometry,则定义圆和矩形这两个子类的语法如下: public class Circle extends Geometry {…} public class Rectangle extends Geometry {…}
6.1 继承:父类和子类 继承示例:几何类、圆、矩形的实现和测试 父类:几何类,Geometry.java public class Geometry { } 子类:圆,Circle.java class Circle extends Geometry { } 子类:矩形,Rectangle.java class Rectangle extends Geometry {
6.1 继承:父类和子类 继承示例:几何类、圆、矩形的实现和测试 测试类:TestCircleAndRectangle.java public class TestCircleAndRectangle { public static void main(String[] args) { Circle circle = new Circle(1); System.out.println("颜色是: " +circle.getColor()); Rectangle rectangle = new Rectangle(2, 4);… }
6.1 继承:父类和子类 关于继承几个主要的关键点 子类从它的父类中继承可访问的属性和方法,在继承父类的基础上,扩展自己的属性和方法,通常比它的父类包含更多的属性和方法。 子类并不是父类的一个子集。实际上,一个子类通常比它的父类包含更多的信息和方法。 父类中的私有数据在该类之外是不可访问的。因此,不能在子类中直接使用,但可以通过公共的访问器或修改器来访问或修改它们。
6.1 继承:父类和子类 关于继承几个主要的关键点 在Java中不允许多重继承,一个子类只能继承一个父类,不能继承多个父类,这种继承称为单一继承。 继承是用来表达is-a“是一种”的关系,一个父类和它的子类之间必须存在is-a“是一种”的关系。比如:代码中的几何图形和圆与矩形,圆是几何图形,矩形是几何图形,还有:人是动物,人和动物是继承的关系。 但不是所有is-a“是一种”的关系都该用继承来实现。例如:正方形是一种矩形,是不是就可以让正方形继承于矩形呢?显然,不适合,因为矩形的宽和高并不适用于正方形。
6.1 继承:super关键字 super关键字:父类的构造方法能继承吗? 构造方法用于构建一个类的实例,不同于属性和普通方法,父类的构造方法是不能被子类继承。 它们只能从子类的构造方法中用关键字super调用。如果没有显式使用super,那么将自动调用父类的无参构造方法。 调用父类构造方法的语法如下: super()或super(parameters); 注意:super是指代父类。要调用父类的构造方法必须使用这个关键字,而且这个调用必须是构造方法的第一条语句。
6.1 继承:super关键字 super关键字:构造方法链
6.1 继承:super关键字 super关键字:构造方法链 在任何情况下,构造一个类的实例时,将会调用沿着继承链的所有父类的构造方法。当构造一个子类的对象时,子类构造方法会在完成自己的任务之前,首先调用它的父类的构造方法。如果父类继承自其他类,那么父类构造方法又会在完成自己的任务之前,调用它自己的父类的构造方法。这个过程持续到沿着这个继承体系结构的最后一个构造方法被调用为止。这就是构造方法链(constructor chaining)。
6.1 继承:super关键字 程序清单:Faculty.java public class Faculty extends Employee {//职员类 public static void main(String[] args) { new Faculty(); } public Faculty() { System.out.println("(4) 调用无参的构造方法Faculty()。"); }}
6.1 继承:super关键字 程序清单Faculty.java class Employee extends Person { //雇员类 public Employee() { this("(2) 调用员工的重载构造函数Employee()。"); System.out.println("(3) 调用无参的构造方法Employee()。"); }
6.1 继承:super关键字 程序清单Faculty.java public Employee(String s) { System.out.println(s); }} class Person {//人 public Person() { System.out.println("(1) 调用无参的构造方法Person()。"); (1) 调用无参的构造方法Person()。 (2) 调用员工的重载构造函数Employee()。 (3) 调用无参的构造方法Employee()。 (4) 调用无参的构造方法Faculty()。
6.1 继承:super关键字 super关键字:构造方法链 上面程序构造方法链调用过程的图形显示。
6.1 继承:super关键字 super关键字:调用父类的方法 关键字super不仅可以引用父类的构造方法,还可以引用父类的方法。语法是: 在Circle.java类中的printCircle()方法中使用super改写程序如下: public void printCircle(){ System.out.println("这个圆创建的时间是:" + super.getDateCreated() + ",其半径是:" + radius); }
6.1 继承:方法重写与重载 方法重写 重写/覆盖(Overriding):子类从父类中继承了方法,但有时子类需要修改父类中所定义的方法。 要重写一个方法,需要在子类中使用和父类一样的签名以及一样的返回值类型来对该方法进行定义。 public class Circle extends Geometry { public String toString() {// 重写方法 toString() return super.toString() + "\nradius is " + radius; }
6.1 继承:方法重写与重载 方法重写 当子类从父类中继承方法时,有时需要修改父类中的方法的实现,这称为方法的重写。在JAVA中,定义了一个重写/覆盖(Overriding)的标注@Override,放在子类的方法前面,表示重写了父类的方法。 public class Circle extends GeometricObject { @Override public String toString() { return super.toString() + "\nradius is " + radius;}}
6.1 继承:方法重写与重载 方法重写 仅当实例方法是可访问时,它才能被覆盖。这样,因为私有方法在它的类本身以外是不能访问的,所以不能覆盖它。如果子类中定义的方法在父类中是私有的,那么这两个方法完全没有关系。 与实例方法一样,静态方法也能被继承。但是,静态方法是不能被覆盖或重写的。如果父类中定义的静态方法在子类中被重新定义,那么在父类中定义的静态方法将被隐藏,可以使用语法:父类名.静态方法名调用隐藏的静态方法。
6.1 继承:方法重写与重载 方法重载 重载(Overloading),是指可以定义一些名称相同的方 法,通过定义不同的输入参数来区分这些方法,然后再调用时,JAVA虚拟机就会根据不同的参数样式,来选择合适的方法执行。 比如:前面我们定义的无参构造方法和带参数的构造方法就是方法的重载。 CircleV02(){ …} CircleV02(double newRadius){ … }
6.2 多态 什么是多态? 多态是面向对象程序设计的三个特征(封装、继承和多态)之一。 顾名思义,多态就是指同一种行为在不同的对象上有不同的表现形式。 为了更好地理解多态的概念,首先来定义两个术语:子类型和父类型,子类定义的类型就是子类型,父类定义的类型就是父类型。这样,Circle是Geometry的子类型, Geometry是Circle的父类型;接下来分析TestPolymorphism.java。
6.2 多态 什么是多态? public static void main(String[] args) { displayObject(new Circle(1, "红", false)); displayObject(new Rectangle(1, 1, "黑", true)); } public static void displayObject(Geometry object) { System.out.println("创建时间: " + object.getDateCreated() + ",颜色是: " + object.getColor()); }}
6.2 多态 什么是多态? 方法displayObject(Geometry object)具有Geometry类型的参数,而调用这个方法的时候其参数可以是Geometry类型的任何实例,比如上面的代码中: new Circle(1, "红", false) new Rectangle(1, 1, "黑", true) 这样,父类型的引用可以指向任何子类型的对象,这就是多态。 多态:父类型的引用可以指向任何子类型的对象。
6.2 多态 什么是动态绑定? 先看代码: Object o = new Geometry(); System.out.println(o.toString()); 思考:o这个引用变量调用哪个toString()方法? 两个术语:声明类型和实际类型。变量声明的类型就是声明类型;实际类型是被变量引用的对象的实际类。变量o的声明类型为Object, Object就是声明类型;o指向new Geometry()创建的对象,则o的实际类型是Geometry。
6.2 多态 什么是动态绑定? 再比如: Object o1 = new Circle(1); System.out.println(o1.toString());//调用圆的toString() o调用哪个toString()方法是由o的实际类型决定的,是指在运行时由JVM动态调用的,这就是动态绑定(dynamic binding)。
6.2 多态 动态绑定的工作机制 假设对象o是类C1、 C2、 ...、Cn的实例,其中C1 是C2的子类, C2 是C3的子类, ...。在Java中,Cn是Object类。如果对象o调用一个方法p,那么Java虚拟机依次在类C1、 C2、 ...、Cn中查找方法p的实现,直到找到,就停止查找并调用。
6.2 多态 动态绑定的工作机制 匹配方法的签名和绑定方法的实现是两个独立的事情。引用变量的声明类型决定了编译时匹配哪个方法。编译器会在编译时,根据参数类型、参数个数和参数顺序找到匹配的方法。 一个方法可能在几个子类中都被实现。 Java虚拟机在运行时动态绑定方法的实现,这是由变量的实际类型决定的。
6.2 多态 对象转换 继承关系的父类对象和子类对象之间可以进行转换。 隐式转换(implicit casting)也叫向上转换(upcasting) 子类型->父类型 如:Object o = new Student(); 显示转换(explicit casting)也叫向下转换(downcasting)或强制转换。 父类型->子类型 如:Student b = (Student)o;
6.2 多态 类型转换 类型转换,可以用水果、苹果、橘子之间的关系来进行解释 水果类Fruit,是苹果类Apple和橘子类Orange的父类,苹果类Apple和橘子类Orange 是水果的子类。 苹果是水果,橘子是水果,所以,总是可以将苹果和橘子的实例安全地赋值给Fruit变量。 水果就是苹果?水果就是橘子? 显然不能这么说,所以,就必须进行显式转换才能将Fruit 的实例赋值给Apple的变量。
6.2 多态 instanceof运算符 instanceof 运算符是用在运行时指出对象是否是特定类的一个实例。 语法: result = object instanceof class 返回一个布尔值,true,则object 是 class 的一个实例;false,则object 不是指定类的一个实例,或者 object 是 null 。 参数:Result:布尔类型;Object:必选项,任意对象表达式;Class:必选项,任意已定义的对象类。
6.3 访问权限修饰符 访问权限(可见性)修饰符回顾 前面已使用的修饰符有:public、private,缺省。 private:用于修饰成员属性和方法,表明只能在类内部访问它们,如其它类要访问其属性,必须使用get/set方法。 缺省:没有任何修饰符。类没有修饰符,表明可以被同一个包下的不同类访问;属性和方法没有修饰符,表明可以被同一个包下的不同类访问和调用。
6.3 访问权限修饰符 protected修饰符 由于类之间继承关系的存在,经常需要允许子类访问定义在父类中的数据和方法,但又不允许非子类访问这些数据和方法。此时,就可使用protected修饰符,这样父类中被保护的数据或方法可以在它的子类中访问。 示例:程序Person.java,Person类有受保护字段和方法:protected String name;protected String getName(); 程序Student.java和程序Teacher.java中的类继承于Person类,程序TestProtected.java演示父类和子类中protected修饰符。
6.3 访问权限修饰符 protected修饰符 Protected类型可以在本包和子类中访问。在本包中访问,顾名思义,可以在定义类的包中的任何地方申请一个包含protected修饰符的域和方法的类的对象,并通过这个对象访问被protected修饰的域和方法。 在子类中访问,无论是否在本类的包中,都可以在子类中调用被protected修饰的域和方法。调用的形式为: super.XXX 其中XXX为超类的域或方法,
6.3 访问权限修饰符 访问权限修饰符小结 权限由大到小: public--->protected--->缺省(没有修饰符)--->private 修饰符 同一个类 同包,子类 同包,非子类 不同包,子类 不同包,非子类 private √ default protected public
6.3 访问权限修饰符 final修饰符 在有些情况下,为了防止类扩展,就可以使用final修饰符,表明这是个最终类,是不能作为父类的。 比如:Math类就是个最终类,public final class Math final修饰符可以用在类和类的成员(数据和方法)上,只有final修饰符还可以用在方法中的局部变量上。方法内的终极局部变量就是常量。 final修饰的方法不可被重写。
6.3 访问权限修饰符 final修饰符 1、final的变量的值不能够被改变 ①、final的成员变量 源代码: TestFinal.java
小结: 什么是继承?如何表达继承关系?关键字extends super关键字 多态,动态绑定,类型转换 作业 见课程网站