一、抽象过程
Alan Kay曾经总结了第一个成功的面向对象语言、同时也是Java所基于的语言之一的SmallTalk的五个基本特性,这些特性表现了一种纯粹的面向对象程序设计方式:
1、万物皆为对象。 奇特的变量。可以存储数据,也可以有自身的执行操作。理论上讲,你可以抽取待求解问题的任何概念化构件(狗狗、建筑物、服务等),将其表示为程序中的对象。
2、程序是对象的集合,它们通过发送消息来告知彼此所要做的。要请求一个对象,就必须发送一条消息。具体说,消息是某个特定对象的方法的调用请求。
3、每个对象都有自己的由其他对象所构成的存储。具体说:可以通过创建包含现有对象的包的方式来创建新类型的对象。因此,可以在程序中构建复杂的体系,同时将其复杂性隐藏在独享的简单性背后。
4、每个对象都拥有其他类型。“每个对象都是某个类(class)的一个实例(instance)”。每个类最重要的区别于其他类的特性就是“可以发送什么样的消息给它。”
5、某一特定类型的所有对象都可以接收同样的消息。例如:“圆形”类型的对象同时也是“几何形”类型的对象,所以一个“圆形”对象必定能够接收发送给“几何形”对象的消息。这意味着可以编写与“几何形”交互并自动处理所有与几何形性质相关的事物的代码。这种可替代性是OOP中最强有力的概念之一。
二、每个对象都有一个接口
所有对象都是唯一的,但同时也是具有相同的特性和行为的对象所归属的类的一部分。
类描述了具有相同特性(数据元素)和行为(功能)的对象的集合,所以一个类实际上就是一个数据类型,例如所有浮点型数字具有相同的特性和行为集合。二者的差异在于,程序员通过定义类来适应问题,而不再被迫只能使用现有的用来表示机器中的存储单元的数据类型。可以根据需求,通过添加新的数据类型来扩展变成语言。
面向对象向程序设计的挑战之一,就是在问题空间的元素和解空间的对象之间创建一对一的映射。
三、每个对象都提供服务
对象看作是服务提供者,它将通过调用其他对象提供提供的服务来实现这一目的。还有一个附带的好处:它有助于提高对象的内聚性。高内聚是软件设计的剧本质量要求之一:这意味着一个软件构件的各个方面“组合”得很好。
在良好的面向对象设计中,每个对象都可以很好地完成一项任务,但是他并不试图做更多的事。
四、被隐藏的具体实现
将程序开发人员按照角色分为类创建者和客户端程序员(那些在其应用中使用数据类型的消费者)。
类创建者的目标是构建类,只向客户端程序员暴露必需的部分,而隐藏其他部分。将实现隐藏起来可以减少程序bug。
访问控制的原因一:让客户端程序员无法触及他们不应该触及的部分——这些部分对数据类型的内部操作来说是必需的,但并不是用户解决特定问题所需的接口的一部分。
访问控制的原因二:允许库设计者可以改变内部的工作方式而不用担心会影响到客户端程序员。
Java的三个内部设定边界的关键字:public private protected:
public:任何都可用
private:用于创建类型和类型内部的方法;
protected:作用于private相当,另外继承的类也可以访问protected成员;
注意:默认的访问权限,即没用访问指定词。被称为包访问权限。同一个包中其他类的成员可以访问。
五、复用的具体实现
代码复用是面向对象程序设计语言所提供的最了不起的优点之一。
最简单的复用某个类的方式是直接使用该类的一个对象【new】,也可以将类的一个对象置于某个新类中,即创建一个成员对象。
新的类可以由任意数量、任意类型的其他对象以任意可以实现新的类中想要的功能的方式所组成。
使用现有的类组成新的类,这种概念被称为组合【composition】;如果组合是动态发生的,那么通常被称为聚合【aggregation】。组合经常被视为“has-a”(拥有)关系。
组合有极大的灵活性:
1、可以在不干扰现有客户端情况下,修改被private修饰的成员【被private修改时的成员,不能被访问】,也可以在运行时修改类的成员,实现动态修改程序的行为。
2、比继承要灵活的多。因为编译器必需通过继承而创建的类施加编译时的限制。 处处都应用继承,会导致难以使用并过分复杂;实际上,在建立新类时,应该首先考虑组合,会更灵活。
六、继承
编程语言的基本单位是类,用关键字class表示,主要是数据和功能的封装。
具有相似的数据的一些包含类,可以考虑到使用继承,从而避免繁琐。例如,动物和蛇。
继承,以现有的类为基础,复制它,然后通过添加和修改这个副本来创建新类的过程。不过,当源类(也被称为基类、超类或父类)发生改变时候,被修改的“副本”(被称为导出类、继承类或子类)也会反应出这些变动。
通过继承而产生的类型等价性是理解面向对象程序设计方法内涵的重要门槛。
导出类继承基类后,产生差异的两种方法:
1、在导出类中添加新的方法,新方法不是基类的一部分。但是应该仔细考虑是否存在基类也需要这些额外方法的可能性。
2、改变现有基类的方法的行为,即覆盖(overriding)某个方法。
六.1 、“是一个”与“像是一个”关系
1、是一个(is-a):导出类只是覆盖基类中的方法,即导出类和基类是完全相同的类型,结果可以用一个导出类对象来完全替代一个基类对象。这被视为【纯粹替代】,通常称之为【替代原则】。
2、像是一个(is-like-a):导出类中添加了新的成员。基类无法访问新类中的方法。
七、伴随多态的可互换对象
向上转型:将导出类看作其基类的过程。
八、单根继承结构
在Java中,所有类的最终的都继承自单一的基类。这个终极基类的名字就是Object。这种现象是单根继承结构。
九、容器
容器:数组类型的来实现相同的功能。也成为集合。
容器的参数化类型:一个编译器可以自动定制作用于特定类型上的类。Java SE5新增了参数化类型,在Java中称之为泛型。一对尖括号,中间包含类型信息,通过这些特征就可以识别对泛型的使用。例如:
List<Shape> list=new ArrayList<Shape>();
十、对象的创建和生命期
使用对象,最关键的问题之一是对象的生成和销毁方式。对象的生成需要资源,尤其是内存。当对象不再需要的时候,必须清理掉,使其占有的资源可以被释放和重用。
对象的数据位于何处?怎样控制对象的生命周期?有两种方式:
第一种方式:C++认为效率控制是最重要的议题,所以给程序员提供了选择的权力。为了追求最大的执行速度,对象的存储空间和生命周期可以在编写程序时确定,这可以通过将对象置于堆栈(它们有时被称为自动变量或限域变量)或静态存储区域内实现。
优点:将存储空间分配和释放置于优先考虑的位置。
缺点:不灵活。必须在编写程序时知道对象确切的数量、生命周期和类型。
第二种方式:在被称为堆(heap)的内存池中动态创建对象。
优点:直到运行时才知道需要多少对象,它们的生命周期如何,以及它们具体类型是什么。这些问题的答案只能在程序运行时相关代码被执行到的那一刻才能确定。如果需要一个新对象,需要时直接在堆中创建。
缺点:存储空间是在运行时被动态管理的,因此需要大量的时间在堆中分配存储空间,这可能远远大于在堆栈中创建存储空间的时间。
在堆栈中创建存储空间和释放存储空间通常个需要一条汇编指令即可,分别对应将栈顶指针向下移动和将栈顶指针向上移动。创建堆存储空间的时间依赖于存储机制的设计。
Java完全采用了动态内存分配方式。创建新对象时,使用new关键字来构建此对象的动态实例。
知识补充:
堆栈负责保存我们的代码执行(或调用)路径,而堆则负责保存对象(或者说数据,接下来将谈到很多关于堆的问题)的路径。
栈和堆中主要放置了四种类型的数据:值类型(Value Type),引用类型(Reference Type),指针(Pointer),指令(Instruction)。
Java提供了被称为“垃圾回收器”的机制,它可以自动发现对象核实不再被使用,并继而销毁它。垃圾回收器可以减少了所必须考虑的议题和必须编写的代码,且更高层的保障了避免暗藏的内存遗漏的问题。【c++中常见的问题】