多态
多态意味着父类型的变量可以引用子类型的对象
基类型对象访问派生类重写的方法;循环调用基类对象,访问不同派生类;——滕国栋
首先明确一下两个概念:向上转型和向下转型
向上转型
human 是 man 的基类
man a = new man()
b = new human()
a = b
向下转型
human b = new human()
man a = new man()
b = a
继承关系使一个子类能继承父类的特征,并且附加一些新特征。子类是它的父类的特殊化,每个子类的实例都是其父类的实例,但是反过来不成立。例如:每个圆都是一个几何对象,但并非每个几何对象都是圆。因此,总可以将子类的实例传给需要父类型的参数。考虑以下程序中的代码。
public class PolymorphismDemo {
public static void main(String[] args) {
displayObject(new Circle(1, "red", false));
displayObject(new Rectangle(1, 1, "black", true));
}
//Circle,Rectangle都是GeometricObject的子类
public static void displayObject(GeometricObject object) {
System.out.println("Create on " + object.getDateCreated() + ". Color is " + object.getcolor());
}
}
// 运行结果
// Create on Fri Nov 26 15:46:30 CST 2021. Color is red
// Create on Fri Nov 26 15:46:30 CST 2021. Color is black
方法displayobject 具有Geometricobject类型的参数。可以通过传递任何一个Geometricobject的实例(例如:在new Circle(1,“red”, false)和new Rectangle(1,1,“black”,true))来调用displayobject,使用父类对象的地方都可以使用子类的对象。这就是通常所说的多态( polymorphism,它源于希腊文字,意思是“多种形式")。简单来说,多态意味着父类型的变量可以引用子类型的对象。
如果子类和父类都有相同的方法,例如
Object o = new GeometricObject();
System.out.println(o.toString());
如何决定运行时调用哪个方法
动态绑定
调用哪个toString()呢?为了回答这个问题,我们首先介绍两个术语:声明类型和实际类型。一个变量必须被声明为某种类型。变量的这个类型称为它的声明类型( declared type)。这里, 。的声明类型是object,一个引用类型变量可以是一个nul1值或者是一个对声明类型实例的引用。实例可以使用声明类型或它的子类型的构造方法创建。变量的实际类型(actual type)是被变量引用的对象的实际类。这里, 。的实际类型是Geometricobject,因为。引用使用new Geometricobject()创建的对象。。调用哪个. toString()方法由。的实际类型决定。这称为动态绑定(dynamic binding)。动态绑定工作机制如下:假设对象。是类C, c2, …, Ca.1, C,的实例,其中C,是C2的子类, C,是C的子类, …, Cn.1是C,的子类,如图11-2所示。也就是说, C,是最通用的类,C,是最特殊的类。在Java中, G,是object类。如果对象。调用一个方法p,那么JVM会依次在类C, C2, …, Cni1, C,中查找方法p的实现,直到找到为止。一旦找到一个实现,就停止查找,然后调用这个首先找到的实现。以下给出一个演示动态绑定的例子。
public class DynamicBindingDemo {
public static void main(String[] args) {
m(new GraduateStudent());
m(new Student());
m(new Person());
m(new Object());
}
public static void m(Object x) {
System.out.println(x.toString());
}
}
class GraduateStudent extends Student {
}
class Student extends Person {
@Override
public String toString() {
return "Student";
}
}
class Person extends Object {
@Override
public String toString() {
return "Person";
}
}
// 运行结果:
// Student
// Student
// Person
// java.lang.Object@2d363fb3
方法m 有一个object类型的参数。可以用任何对象(例如:new GraduateStudent()、 new Student()、 new Person()和new object())作为参数来调用m方法。当执行方法m(object x)时,调用参数x的tostring方法。x可能是GraduateStudentStudent, Person或者object的实例。类Student, Person以及Object都有它们自己对toString方法的实现。使用哪个实现取决于运行时×的实际类型。调用m(new GraduateStudent())会导致定义在Student类中的toString方法被调用调用m(new Student())会调用在Student类中定义的toString方法。调用m(new Person())会调用在Person类中定义的toString方法。调用m(nenobject()) 会调用在object类中定义的toString方法。匹配方法的签名和绑定方法的实现是两个不同的问题。引用变量的声明类型决定了编译时匹配哪个方法。在编译时,编译器会根据参数类型、参数个数和参数顺序找到匹配的方法。一个方法可能在沿着继承链的多个类中实现。Java虚拟机在运行时动态绑定方法的实现, 这是由变量的实际类型决定的。
操作符instanceof
考虑一下代码:
void someMethod(Object myObject){
if (myObject instanceof Circcle){
System.out.println("The circle diameter is " +
((Circle)myObject).getDiameter());
}
}
你可能会奇怪为什么必须进行类型转换。变量myobject被声明为object。声明类型决定了在编译时匹配哪个方法。使用myobject. getDiameter()会引起一个编译错误,因为object类没有getoiameter方法。编译器无法找到和myobject.getDiameter()匹配的方法。·所以,有必要将myobject转换成Circle类型,以告诉编译器myobject也是Circle的一个实例。为什么不在一开始就把myobject定义为Circle类型呢?为了能够进行通用程序设计一个好的做法是把变量定义为父类型,这样,它就可以接收任何子类型的对象。
总结
1.多态现实意义理解:
现实事物经常会体现出多种形态,如学生,学生是人的一种,则一个具体的同学张三既是学生也是人,即出现两种形态。Java作为面向对象的语言,同样可以描述一个事物的多种形态。如Student类继承了Person类,一个Student的对象便既是Student,又是Person。
2.多态体现为父类引用变量可以指向子类对象。
3.前提条件:必须有子父类关系。
注意:在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法。
4.多态的定义与使用格式
定义格式:父类类型 变量名=new 子类类型();
5.动态绑定
JVM在o的子类中找到第一要求的方法名就对他经行操作
6. 操作符instanceof
用于判断类型转换后的对象是否为判断对象
7.理解:
多态是同一个行为具有多个不同表现形式或形态的能力。
多态就是同一个接口,使用不同的实例而执行不同操作。