Java多态基础知识完整的归纳总结

面向对象程序设计的三大支柱是封装、继承和多态。这篇文章带你了解多态。

关键点:多态意味着父类型的变量可以引用子类型的对象;使用父类对象的地方都可以使用子类的对象

必要的知识背景

1.子类型和父类型
一个类实际上定义了一种类型。

  • 子类定义的类型称为子类型(subtype);
  • 父类定义的类型称为父类型(supertype)。
    例如:若Circle extends GeometricObject
    则可以说:Circle 是 GeometricObject的子类型,而 GeometricObject 是 Circle 的父类型

2.实际类型和声明类型

  • 一个变量必须被声明为某种类型。变量的这个类型称为它的声明类型
  • 一个引用类型变量可以是一个null值 或者是一个对声明类型实例的引用。实例可以使用声明类型或它的子类型的构造方法创建。变量的实际类型是被变量引用的对象的实际类
  • 例如下面代码中,
    o的声明类型是Object,
    o的实际类型是GeometricObject, 因为o指向使用new GeometricObject()创建的对象
Object o = new CeometricObject() ; 

此篇文章对此有详细介绍:
java对象创建与引用变量的详解
3. 继承关系使一个子类继承父类的特征,并且附加一些新特征。子类是它的父类的特殊化,每个子类的实例都是其父类的实例,但是父类不是子类的是实例。
此篇文章对此有详细介绍:Java继承入门全部知识归纳总结

动态绑定

要点提示:方法可以在沿着继承链的多个类中实现。JVM 决定运行时调用哪个方法

我们知道方法可以在父类中定义而在子类中重写。例如:toString()方法是在Object类中定义 的,而在GeometricObject类中重写。思考下面的代码:

 Object o = new CeometricObject() ; 
 System.out.println(o.toString()); 

这里的o调用哪个toString()呢?由上面可知o的实际类型是GeometricObject, 所以调用的是CeometricObject中的toString.
也就是调用哪个 toString()方法由o的实际类型决定。这就称为动态绑定。

动态绑定的运行机制

动态绑定工作机制如下:
假设对象o是类C1,C2, … Cn-1, Cn的实例,其中C1是C2的子类,C2是C3的子类,… Cn-1是Cn的子类,如图下图所示。也就是说,Cn 是最通用的类,C1 是最特殊的类。
在这里插入图片描述
在Java中,Cn 是object类。如果对象o调用一个方法p,那么JVM会依次在类C1, C2, … Cn-1, Cn中查找方法p的实现,直到找到为止。一旦找到一个实现,就停止查找,然后调用这个首先找到的实现。这就是动态绑定的过程。

注意

  1. 匹配方法的签名和绑定方法的实现是两个不同的问题。引用变量的声明类型决定了编译时匹配哪个方法。在编译时,编译器会根据参数类型、参数个数和参数顺序找到匹配的方 法。一个方法可能在沿着继承链的多个类中实现Java虚拟机在运行时动态绑定方法的实现, 这是由变量的实际类型决定的
  2. 小总结:
    java虚拟机在匹配方法时:
    首先是静态绑定,识别重载方法(也就是匹配方法签名)和final、static、private等重写方法;
    然后是动态绑定,根据对象的实际类型,自下向上匹配重写函数链;

对象转换和instance of 运算符

对象转换:对象的引用可以类型转换为对另外一种对象的引用,这称为对象转换。

隐式转换:

例如:

Object o = new Student(; // Implicit casting

Student 的实例也是 Object 的实例, 这条语句把Student类型隐式转换为Object的引用。

显示转换:

显然 Student b = o ;这条语句是错误的,因为Student对象总是object的实例,但是,object 对象不一定是Student的实例。
但如果o实际上是一个Student的对象,就可以使用显式转换,将o赋引用给Studen变量,如下所示:

 Student b = (Student)o; // Explicit casting

这就是显示转换,它的语法与基本类型转换的语法很类似,用圆括号把目标对象的类型括住,然后放到要转换的对象前面。

注意:

  1. 对象成员访问运算符(.)优先于类型转换运算符。使用圆括号保证在点运算符(.) 之前进行转换,例如: ((Circle)object).getArea();
  2. 对基本类型值进行转换不同于对对象引用进行转换。转换基本类型值返回一个新的值。 而转换一个对象引用不会创建一个新的对象,例如:
Object o= new Circle();
Circle C = (Circle)o; // No new object is created

现在,引用变量o和c指向是同一个对象。

instanceof

  • 总是可以将一个子类的实例转换为一个父类的变量, 称为向上转换(upcasting) , 因为子类的实例永远是它的父类的实例。
  • 当把一个父类的实例转换为它的子类变量(称为向下转换 (downcasting)时, 必须使用转换记号 “ (子类名) ” 进行显式转换, 向编译器表明你的意图。
  • 为使转换成功,必须确保要转换的对象是子类的一个实例。 如果父类对象不是子类的一个 实例, 就会出现一个运行异常 ClassCastException。例如:如果一个对象不是 Student 的实例, 它就不能转换成 Student 类型的变量。 因此, 一个好的经验是, 在尝试转换之前确保该 对象是另一个对象的实例。 这是可以利用运算符 instanceof 来实现:

举例:

Object myObject = new Circle();
// Some lines of code
/** Perform casting if my0bject is an instance of Circle */
if (myObject instanceof Circle) {
    
    
System. out. println("The circle diameter is”+
((Circle)myObject) . getDiameter()); .
...
}

说明:myObject instanceof Circle 表示,如果myObject 是Circle的一个实例,就放回true,否则放回false;

多态的一个应用——equals方法

如同toString()方法,equals(Object)方法是定义在Object类中的另外一个 有用的方法。

它的签名是:

public boolean equals(Object o) 

这个方法测试两个对象是否相等。调用它的语法是:

object1.equals(object2);

Object类中equals方法的默认实现是:

  public boolean equals(Object obj) {
    
     return (this == obj); }

这个实现使用==运算符检测两个引用变量是否指向同一个对象。因此,应该在自己的 客户类中重写这个方法, 以测试两个不同的对象是否具有相同的内容。
可以重写Circle类中的equals方法, 基于圆的半径比较两个圆是否相等,如下所示:

public boolean equals(Object o) {
    
    
if (o instanceof Circle)
return radius == ( (Ci rc1e)o). radius ;
else
return this = o;
}

比较运算符 “== ”用来比较两个基本数据类型的值是否相等,或者判断两个对象是否具有相同的引用。 如果想让 equals 方法能够判断两个对象是否具有相同的内容,可以在定义这些对象的类时,重写 Object类中的 equals 方法。运算符 “= =”要比equals 方法的功能 大些,因为“ = =”运算符可以检测两个引用变量是否指向同一个对像。

注意: 在子类中, 使用签名equals(SomeClassName obj) (例如: equals(Circle c))重写equals方法是一个常见错误,应该使用equals(Object obj)。

友情链接
Java面向对象,这些你一定想再来复习复习:

  1. Java继承入门全部知识归纳总结
  2. Java接口入门全部知识总结
  3. Java抽象类和抽象方法归纳总结
  4. Java可见性修饰符public/private/protected的归纳总结

猜你喜欢

转载自blog.csdn.net/qq_45768060/article/details/105967012
今日推荐