对象的概念

抽象

所有编程语言都提供抽象机制,OOP允许我们根据问题来描述问题,而不是根据运行解决方案的计算机。

Alan Kay总结的Java语言的五大基本特征:

  • 万物皆对象
  • 程序是一组对象,通过消息传递来告知彼此该做什么、
  • 每个对象有自己的存储空间,可容纳其它对象
  • 每个对象都有一种类型
  • 同一类所有对象都能接收所有相同的消息(对象的可替换性)

Grady Booch提供了对对象更简洁的描述:一个对象具有自己的状态,行为和标识。这意味着对象有自己的内部数据(提供状态)、方法(产生行为),并彼此区分(每个对象在内存中都有唯一的地址)。

接口

  • class和type是一个意思
  • 面向对象程序设计时,最大的问题就是如何在“问题“和”建模“之间建立理想的”一对一“的映射关系。
  • 必须有一种办法能向对象发出请求,令其解决一些实际的问题。我们向对象发出的请求是通过它的“接口”(interface)定义的,对象的“类型”或“类“则规定了它的接口形式。”类型“与”接口“的对应关系是面向对象程序设计的基础。

服务提供

在开发或理解程序设计时,我们可以将对象看成是“服务提供商”。你的程序本身将为用户提供服务,并且它能通过调用其他对象提供的服务来实现这一点。我们的最终目标是开发或调用工具库中已有的一些对象,提供理想的服务来解决问题。

我们可以将问题一一分解,抽象成一组服务。软件设计的基本原则是高内聚:每个组件的内部作用明确,功能紧密相关。

封装

编程的侧重领域分为研发应用。应用程序员调用研发程序员构建的基础工具类仅向程序员公开必要的内容,并隐藏内部的细节。

使用访问控制的原因有以下两点:

  1. 让应用程序员不要触摸他们不应该触摸的部分
  2. 使类库的创建者在不影响后者使用的情况下完善更新工具库。

Java中通过关键字来设置类中的访问权限:

  • public 任何人都能访问
  • private 只有类本身和类内部的方法才能访问
  • protected 与private不同的是,子类可以访问protected的成员
  • default 包访问

复用

组合:Composition,经常用来表示“拥有“关系

聚合:Aggregation,动态的组合

区分两者的方法:聚合关系中,整件不会拥有部件的生命周期,所有整件删除时,部件不会被删除;组合关系中,整件拥有部件的生命周期,部件删除时,组件也会跟着删除。而且,多个组件不会共享同一个部件。

在创建新类时,首先要考虑“组合”,因为它更简单,而且设计更加清晰。

继承

继承不完全等同于克隆,在继承过程中,若原始类发生了变化,修改过的“克隆类”也会反映出变化。

派生类指向基类。

继承通常意味着两件事情:添加新方法或者覆盖原方法。

判断是否继承,就看你的类之间是否有is-a关系。

有时,你在派生类添加了新的接口元素,从而扩展接口。虽然新类型仍然可以替代基类,但是这种替代不完美,原因在于基类无法访问新添加的方法。这种关系称为is-like-a关系。新类型不但具有旧类型的接口,而且不包含其它方法,所以不能说新旧类型完全相同。1545764820176

多态

我们在处理类的层次结构时,通常把一个对象看成是它所属的基类,而不是把它看成具体类。通过这种方法,我们可以编写出不局限于特定类型的代码。这样的代码不会受添加的新类型影响,并且添加新类型是扩展面向对象程序以处理新情况的常用方法。通过派生新的子类扩展设计的这种能力是封装变化的基本方法之一。
Bird-example
以前采用早期绑定,意味着编译器生成对特定函数名的调用,该调用会被解析为将执行的代码的绝对地址。
通过继承,程序直到运行时才能确定代码的地址,因此发送消息给对象时,还需要其他一些方案。为了解决这个问题,面向对象语言使用后期绑定的概念。当对象发送消息时,被调用的代码直到运行时才确定。编译器确保方法存在,并对参数和返回值执行类型检查,但是它不知道要执行的确切代码。
发送消息给对象时,如果程序不知道接收的具体类型是什么,但最终执行实际正确的,这就是对象的“多态性”。面向对象的程序设计语言时通过“动态绑定”的方式来实现对象的多态性的。编译器和运行时系统会负责对所有细节的控制,我妈们只需知道要做什么,以及如何利用多态性来更好地设计程序。

单继承结构

Java中,所有的类都是从Object类继承而来。
Java的单继承有很多好处。由于所有的对象都具有一个公共接口,因此他们最终都属于一个基类。
对于完全面向对象编程,我们必须要构建自己的层次结构,已提供与其它OOP语言同样的便利,Java这样的替代方案会是更好的选择。
另外,单继承的结构使得垃圾收集器的实现更为容易。由于运行期的类型信息会存在于所有的对象中,所以我们永远不会遇到判断不了对象类型的情况。这对于系统级操作尤其重要,例如异常处理。

集合

通常,我们并不知道解决某个具体问题需要的对象数量和持续时间,以及对象的存储方式。在面向对象的设计中,问题的解决方案是:创建一个新类型的对象来引用、容纳其它的对象。“集合“这种类型的对象可以存储任意类型、数量的其它对象,它能根据需要进行自动扩容,我们不用关心过程是如何实现的。
选择使用集合有以下两个原因:
1. 集合可以提供不同类型的接口和外部行为。
2. 不同的集合对某些操作有不同的效率。
在泛型出来之前,所有的对象进入集合中,都会被转型成Object,但是从Object向下转型是不安全的(可能会丢失信息)。每次取出元素都要做额外的“向下转型”对程序和程序员都是一种开销。以某种方式创建集合,以确认保存元素的具体类型,减少集合元素“向下转型”的开销和可能出现的错误,这种解决方案就是“参数化类型机制”——泛型。
ArrayList<Shape> shapes = new ArrayList<>();

对象创建与生命周期

我们在使用对象时,要注意的一个关键问题就是对象的创建和销毁方式。每个对象的生存都需要资源,尤其是内存。为了资源的重复利用,当对象不再被使用时,我们应该及时释放资源,清理内存。

Java使用动态内存分配。每次创建对象时,使用new关键字构建该对象的动态实例。这又带来另一个问题:对象的生命周期。较之堆内存,在栈内存中创建对象,编译器能够确定该对象的生命周期并自动销毁它;然而如果你在堆内存创建对象的话,编译器是不知道它的生命周期的。
Java的内存管理器是建立在垃圾收集器上的,它能自动发现对象不再被使用并释放内存。垃圾收集器的存在带来了极大的便利,它减少了我们之前必须要跟踪的问题和编写相关代码的数量。因此,垃圾收集器提供了更高级别的保险,以防止潜在的内存泄露问题,这个问题使得C++项目没落。

异常处理

异常处理机制将程序错误你直接交给编程语言甚至是操作系统。Exception是一个从出错点thrown后的能被特定类型的异常捕捉程序捕捉的异常处理程序catch的一个对象。它不会干扰程序的正常运行,仅当程序运行出错的时候才被执行。这让我们的代码编写更加简单:不用再反复检查错误了。
最后,“异常机制”提供了一种可靠地从错误状况中恢复的方法,使得我们编写出更健壮的程序。有时,你只需要处理好抛出的异常情况并恢复程序的运行即可,无需退出。
Java的异常处理机制在编程语言中脱颖而出。Java从一开始就内置了异常处理,因此你不得不使用它。这是Java语言接收的错误报告方法。如果没有编写适当的异常处理代码,你就会收到一条编译时错误。这种有保障的一致有时互让程序的错误处理变得更容易。值得注意的是,异常处理并不是面向对象的特性。尽管在面向对象的语言中异常通常由对象表示,但是在面向对象语言之前也存在异常。




posted @ 2020-06-29 22:58  zjy4fun  阅读(866)  评论(0编辑  收藏  举报