人们所能解决的问题的复杂性,取决于能将事物抽象到什么程度。
高等的数学和物理就是对事物高度的抽象。它们在那种极度抽象的层次来研究,一旦获得大的进展,就会对我们日常生活造成极大的影响,比如爱因斯坦的相对论,它的发现对世界的影响就是巨大的。编程也是这样,我们将一些日常事物进行抽象,抽象的水平越高,我们就越可以用简洁的代码去描述它。
Alan Kay曾经总结了第一个成功的面向对象语言、同时也是java所基于的语言之一的Smalltalk的五个基本特性,这些特性表现了一种纯粹的面向对象的设计方式:
万物皆为对象
程序是对象的集合,他们通过发送消息来告知彼此要做何事
每个对象都有自己的由其他对象所构成的存储
每个对象都拥有其类型
某一特定类型的所有对象都可以接收同样的消息
或者我们可以对对象进行更加简洁的描述:对象具有状态、行为和标识。每个对象都有内部数据、方法和唯一的地址。
所有的对象都是唯一的,但同时具有相同的特性和行为的对象所归属的类的一部分。这就比如,天空中有很多麻雀,每个麻雀都是一个独立的个体,但是这些个体都是麻雀,它们都有着麻雀的特性。
每个对象都是一个服务的提供者,我们可以通过调用这个对象中的方法,使用对象中的变量来进行自己的操作。将对象看作是服务提供者还有一个附带的好处:这有助于提高对象的内聚性。高内聚是软件设计的基本质量要求之一,这意味着一个软件结构组织的很好,在这个类中,所有的方法和属性都为了一个功能而生,这些方法和属性聚集在一起,在使用时非常便捷。但是我们在日常设计对象的时候面临的一个问题是,我们常常将过多的功能都塞在一个对象中。
在良好的面向对象设计中,每个对象都可以很好的完成一项任务,他们并不试图做更多的事情。这样的设计会有这另外一个好处:低耦合。因为每个对象都有着自己高度内聚的属性方法,他们都为了做自己的“分内之事”而生,多个不同类型对象间通过将各自的功能拼搭,从而达到这种低耦合的效果。
在计算机网络结构的七层模型中,各个层之间互相封闭,最终完成了从最虚拟的计算机操作到做底层的电子线路操作这整套的网络结构。在它的层与层之间,上一层只能看得到下一层方法的接口,他只能知道如何去调用下一层的方法,而对下一层的具体代码实现并不感兴趣。
在java的代码中,使用了三个关键字在类中设定边界,这三个访问指定词决定了紧跟在其后的内容可以被谁使用:
public: 当前类 当前包 子孙类 其他包
protected: 当前类 当前包 子孙类
默认(无修饰): 当前类 当前包
private: 当前类
代码的复用与组合是面向对象程序设计语言所提供的最了不起的优点之一。
最简单的复用的方式就是:直接使用该类的一个对象。此外,也可以将那个类的一个对象至于某个新的类中,我们称这种复用方式为“创建一个成员对象”。新的类可以由任意数量、任意类型的其他对象以任意可以实现新的类中想要的功能的方式所组合。因为是在使用现有的类合成新的类,所以这种概念被称作组合。
我们通常将组合视作是拥有关系——“has-a”。
相比较很多新手非常喜欢在自己的类中使用继承关系,实际上,在建立新类时应该首先考虑组合,因为它更加灵活。如果采用这种组合的方式,设计会变得更加清晰。
我们就不介绍继承是什么意思了,值得一提的是,当我们在项目中去考虑如何抽象出父类时可以思考多个子类,用多个子类来找出他们的共同点,这样对我们抽象父类很有帮助。
因为继承的关系,所有可以发送给基类对象的消息同时也可以发送给子类对象。这就意味着,实际上,子类与父类具有相同的类型。如果我们只是继承了父类,在父类中只重写方法而不创建新的方法,我们可以称之为纯粹替代,对于纯粹替代,我们用——“is-a”来表示这种关系。对于那些不仅仅继承了父类,而且在父类的基础上添加了新的方法,我们用——“is-like-a”来表示这种关系。
在处理层次结构时,我们经常把一个对象不作为某个特定的对象来处理,而是作为他们的父类来对待。比如,我们需要传入一个列表,这时我们在方法的参数中不需区分到底是ArrayList列表还是LinkedList列表,我们只需写上List列表即可。这种多态性让程序极大的丰富化。
但是说到这里存在一个最简单却又很难说清的问题:比如上述的方法在传入一个列表后要调用add函数,这个方法怎么知道传入的List调用哪个add函数呢?
这个问题的答案,也是面向对象程序设计的最重要的妙诀:编译器不产生传统意义上的前期绑定的函数调用,而是采用后期绑定的方法。一个非面向对象变成的编译器产生的函数调用会引起所谓的前期绑定,这个绑定方法意味着编译器将产生一个具体函数名字的调用,而运行时将这个调用解析到将要被执行的代码的绝对地址。OOP为了解决这个问题使用了后期绑定的概念。当向对象发送消息时,被调用的代码直到运行时才能确定。编译器确保被调用的方法存在,并对参数返回值进行检查,但是并不知道将被执行的确切代码。
在C++中,必须明确的通过关键字virtual来声明希望某个方法具备后期绑定属性所带来的灵活性,也就是说默认情况下并不是后期绑定。而在java中动态绑定是默认行为。
在java中,所有的类最终都继承于单一的基类,这个基类就是Object。事实证明,单根继承结构带来了很多好处。单根继承使得垃圾回收器的实现变得容易的多。由于所有对象都保证具有其类型信息,因此不会因无法确定对象的类型而陷入僵局。
当谈到对象的创建和生命周期时,脑海中不禁浮现出了很多种语言,这些语言对它的处理各不相同。C++认为效率控制是最重要的议题,所以,C++给了程序员选择的权利。可以自己new,但是必须要delete掉new出来的空间,否则会陷入著名的内存泄漏的问题之中。
在java中,所有的对象都是在堆的内存池中动态地创建的。这种方式中,知道运行时才知道需要多少对象,他们的生命周期如何,以及他们的具体类型是什么。这样的问题只有在程序运行时相关代码被执行的那一刻才能确定。所以需要大量的时间在堆中分配存储空间,这可能要远远大于在堆栈中创建存储空间的时间。在堆栈中创建存储空间和释放存储空间通常都仅需要一条指令。创建堆存储空间的时间依赖于存储机制的设计。
异常是一种对象。他从出错地点被“抛出”。并被专门设计用来处理特定类型错误的相应的异常处理器“捕获”。异常处理就像是与程序正常执行路径并行的,在错误发生时执行的另一条路径。因为他是一条完全分离的执行路线,所以它不会干扰正常执行的代码。
值得注意的是:异常处理不是面向对象的特征——尽管在面向对象语言中异常处理常被表示成一个对象。异常处理在面向对象语言出现之前就已经存在了。
以上就是java对象导论的详细介绍的内容,更多相关内容请关注PHP中文网(www.php.cn)!