面向对象的特性——封装、继承、多态

封装


定义L:封装从字面上理解就是包装的意思,是指利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体,数据被保护在抽象数据类型的内部,尽可能的隐藏内部的细节,只保留一些对外接口使之与外部发生联系。系统的其他对象只能通过包裹在数据外面的已经授权的操作来与这个封装的对象进行交流和交互。也就是说用户是无需知道对象内部的细节,但可以通过该对象对外提供的接口来访问对象。

封装的优势


  • 通过隐藏对象的属性来保护对象内部的状态
  • 良好的封装能过减少耦合
  • 提高了代码的可用性和可维护性,因为对象的行为可以被单独的改变或者拓展。

暂时能领悟的只有这几条,似乎还有更多

继承


定义:继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性的继承父类。通过使用继承我们能够费产方便的复用以前的代码,大大的提高开发效率。
继承所描述的是“is-a”的关系,如果有两个对象A和B,若可以描述为“A是B”,则可以表示A继承B,其中B是被继承者,称之为父类或者超类,A是继承者称之为子类或者派生类。
实际上继承者是被继承者的特殊化,它除了拥有被继承者的特性外,还拥有自己独有的特性。例如猫有抓老鼠,爬树等其他动物没有的特性。同时在继承关系中,继承者完全可以替换被继承者,反之则不可以,例如我们可以说猫是动物,但是不能说动物是猫就是这个道理,其实对于这个我们称之为向上转型
面向对象的设计原则:里氏替换原则与向上转型息息相关。
诚然,继承定义了类如何相互关联,共享特性。对于若干个相同或者相识的类,我们可以抽象出他们共有的行为或者属性并将其定义成一个父类,然后用其他类继承父类,他们不仅拥有父类的属性、方法还可以定义自己特有的属性或者方法。

继承特点:

  • 子类拥有父类非private的属性和方法
  • 子类可以拥有自己的属性和方法,即子类可以对父类进行拓展。
  • 子类可以用自己的方式实现父类的方法

构造器


通过前面我们知道子类可以继承父类的属性和方法,除了那些private的外还有一样是子类无法继承的–构造器。
对于构造器而言,他只能够被调用,不能被继承,调用父类的构造方法我们使用super()即可。
构建过程是从父类“向外”扩散的,也就是从父类开始向子类一级一级的完成构建。而且我们并没有显示的引用父类的构造器,否则编译器会报错:无法找到符合父类形式的构造器。
对于子类来说,其构造器的正确初始化是非常重要的,而且当且仅当只有一个方法可以保证这点:在构造器中调用父类构造器来完成初始化,而父类构造器具有执行父类初始化所需要的所有知识和能力。
对于继承而言,子类会默认调用父类的构造器,但是如果没有默认的父类构造器,子类必须要显示的指定父类的构造器,而且必须是在子类构造器中做的第一件事。

protected 关键字

private访问修饰符,对于封装而言,是最好的选择,但这个只是基于理想的世界,有时候我们需要这样的需求:我们需要将某些食物尽可能的对这个世界隐藏,但是仍让允许子类的成员来访问他们。这个时候我们可以使用protected.

向上转型


在上面的继承中我们谈到继承是“is-a”的相互关系,猫继承自动物,所以我们可以说猫是动物,或者说猫是动物的一种。这样将猫看做动物就是向上转型。

谨慎继承


继承的缺陷:

  • 父类变,子类就必须变
  • 继承破坏了封装,对于父类而言,它的实现细节对子类来说都是透明的。
  • 继承是一种强耦合关系

多态


定义:所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定。即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能确定。

多态背后的思想是将“做什么”和“谁去做以及怎样去做”分离开来,也就是将“不变的事物“与”可能改变的事物“分离开来。 – 《JavaScript设计模式与开发实践》
多态最根本的好处在于,你不必再像对象询问“你是还是什么类型”而后根据得到的答案调用对象的某个行为——你只管调用该行为就是了,其他的一切多态机制都会为你安排妥当。 – 《重构:改变既有代码的设计》

也就是说多态最根本的作用就是把过程化的条件分支语句转化为对象的多态性,从而消除这些机构的条件分支语句。