Java多态

多态

关于多态解耦合的理解

  • 子类通过向上转型思想调用转到父类

  • 如果以父类引用作为参数去调用某个方法,那么就实现解耦合了,调用该方法时只与基类有有关。(相当于插板上的三角插口,通过统一的规定插口的规格,每个厂家的电子产品都能用,如果每个厂家的电子产品都要不一样的三角插口,那一个插盘到底要设计多少不同的三角插口呢?)。我们关注的是插板上是插口是不是三角插口,并不关心用三角插口的产品。

  • 这种思想就是开闭原则,面向接口编程。通过接口来拓展新的类型。

    • 开闭原则:改变关闭,拓展打开
  • 多态是一项让程序员“将改变的事物与未改变的事物分离开来”的技术。

方法调用时如何确定基类引用指向那个子类对象呢?

  • 该问题设计绑定的知识

方法调用与绑定

  • 将方法调用同方法主体关联起来称为绑定

    • 前期绑定:在程序执行前进行绑定
    • 后期绑定(又称为动态绑定和运行时绑定):运行时根据对象的类型进行绑定
  • Java中除了static方法和final方法(private方法属于final)外,其余的方法都是动态绑定

  • 所以编译器一直不知道对象的类型,但方法调用机制能够正确的调用相应的方法体。

多态对于private方法的缺陷(P156)

  • 子类并不能覆盖父类的private 方法,因为private方法被默认为final方法、
  • 只有飞private方法才能被覆盖

缺陷:域与静态方法(P157)

  • 所谓域就是子类和父类具有相同名称的public变量:例如 public aa;
  • 在子类中则有两个域,一个是自身的public aa 一个是父类的pulbic aa
  • 在引用子类中的aa 时所产生的默认域并不是父类的aa域,因此要访问父类的aa,必须显示的指出super.aa

静态方法

  • 父类中某个方法是静态的,则其不具备多态性
  • 静态方法是与类,而并非与单个队形相关联的

构造器与多态

  • 构造器是隐式的static方法,所以不具备多态性、

构造器的调用顺序

  • 基类的构造器总在导出类的构造过程中被调用的,而且按照继承的层次逐渐向上链接,以便每个基类的构造器都能得到调用

  • 调用顺序

    • 首先逐层找到最顶层的积累,然后逐层访问构造函数,直到最最后。
    • 找基类是就远原则,而不是就近原则。
  • 因为基类的private变量子类是无法访问的,只能通过基类的构造器方法。而子类是继承基类除构造器外所有信息的,所以要完成子类的构造,必须在子类构造过程中调用基类的构造器。

继承和清理

一般情况

  • 某个对象依赖于其他对象,则清理的顺序于初始化的顺序想相反(初始化:A-->B;清理B-->A),其实按照一般思想:初始化从上到下,清理就是从下往上。

  • 对于基类,先清理到处类,再清理基类

存在嵌套成员对象情况(P161)

  • 要使用引用计数来跟踪依旧访问着共享对象的的对象数量
  • 当跟踪计数器为零时才能清理
  • 这种处理方式是很常见的,删除进程也是采用该策略。只用当该进程的子进程为零时才可以删除。

构造器内部的多态方法的行为

  • 如果在构造器的内部调用正在构造的的对象的某个动态绑定的方法,该导出类对象动态构造的方法还没有完全构造完成 就被调用,可能出现问题。(该方法还没有被构造好就被调用,因为调用的是基类被覆盖后的方法,可以将嵌套调用思想来理解)B依赖A完成初始化,A中调用B,这就可能会出错了。

初始化的实际过程(P163)

  1. 在任何事情发生之前,将分配给对象的存储空间初始化成二进制零(所以导出类的存储空间变量的初始值都是零,其至少经过三轮赋值(一开始初始化为零、基类、导出类构造器))
  2. 调用基类的构造器,调用子类覆盖后的多态函数。
  3. 按照声明的顺序调用成员的初始化方法。
  4. 调用导出类的构造器。
    【注】嵌入一个类内部的对象的初始值为null。

总结

  • 在构造器中尽可能的不要调用其他方法。
  • 构造器中唯一能够安全调用的方法是基类中的final方法(也包括private方法),因为这些方法不会被覆盖。

协议变返回类型(P164)

  • 协议变返回类型:表示在导出类中被覆盖方法可以返回基类方法的返回类型的某种导出类型

用继承进行设计(P166)

  • 在建立新的类是如果首先选择继承,可能会使问题变得复杂,首选的方式应该是“组合”
    • 通用准则:用继承表达行为间的差异,并用字段表达状态上的变化
      1. 通过继承,子类里可以定义新的方法属性,与父类产生了差异
      2. 但用字段表达状态上的变化没理解(有理解的可以探讨)
    • 状态模式(不理解)

纯继承(is-a)和拓展(Is-like-a)

  1. is-a :子类与基类有完全相同的属性和方法,基类可以完全代替子类

    1. 导出类通过向上转型,永远不需要知道正在处理的对象的确切类型,通过动态绑定就可以完成处理
    2. is-a关系并不是设计中的唯一做法
  2. Is-like-a:与基类相比,导出类有基类的所有属性和方法,但还有额外的属性和方法

    1. 优点:可以在基类接口上实现自己特有的部分
    2. 缺点:通过向上转型,基类不能访问导出类特有的部分,这也大大增加耦合度(这个问题该如何解决呢?)

向下转型与运行时类型识别(处理Is-like-a缺点)

  • 在向下转型过程中,我们要知道转型的类型,如果转到错误类型是很危险的
  • Java通过RTTI来对每一次转型进行检测,如果转型错误会抛出ClassCastException错误
  • RTTI还提供试图在向下转型执行确定所要处理的类型,这个在14章会详细解释。

总结

  • 多态是面向对象重要特点之一,多态包含了抽象和继承两个重要的特性。表面理解多态就是同名方法有不同的功能。重载和重写是多态的两种表现形式,但是最能表现的还是重写。

  • 在is-a关系中,通过向上转型,动态绑定,可以完全隔离某个操作与导出类的关系,直接操作基类就行。这种编程思想很重要,其中面向接口编程时很好的表现形式。

  • 多态知识学习中还知道,final方法是没有多态特性的,非private基类变量是可以多态的,但是不是很建议这么做。

  • 多态中还学到类初始化过程。其中最重要的是在调用父类构造器之前,先把子类存储空间赋值为0(这就是为什么很多变量没有赋值,但器初始值为零的原因了),并且在构造器中最好不要调用其他方法。

  • 关于清理的知识。其中嵌套对象的清理较为复杂一些,其解决的方法是很常见的。通过设置全局共享对象数记录的变量来跟踪共享变量的数量,只有当该值为零时才可以将该对象清理。

  • 还有就是向上转型和向下转型分别解决is-a和Is-like-a这两种继承模式的中的方法调用问题。

备注

  • 此为本人学习笔记,其中有些知识在我先进水平还没理解,我特此标明。
  • 其中页为《Java编程思想》的中文页码,有不理解的可以看书深入学习。(这本书确实很经典)
  • 《Java编程思想》中练习题答案网址:http://greggordon.org/java/tij4/solutions.htm

你可能感兴趣的:(Java多态)