Thinking In Java Part02(继承)

1、继承
	有两种方法可以使基类与导出类产生差异。
		第一种:直接在导出类中添加新方法。应该仔细考虑基类也需要这些额外方法的可能性。
		第二种:改变先有基类的方法的行为,这被称为覆盖
1.1、是一个与像是一个关系
	继承应该只覆盖基类的方法(而并不添加在基类中没有的新方法)吗?如果这么做,就意味着导出类和基类是完全相同的类型,因为它们具有完全相同的接口。结果可以用一个导出类对象来完全替代一个基类对象。这可以被视为纯粹替代,通常称之为替代原则。在某种意义上,这是一种处理继承的理想方式。这种情况下的基类与导出类之间的关系称为is-a(是一个)关系,因为可以说“一个圆形就是一个几何形状”。判断是否继承,就是要确定是否可以用is-a来描述类之间的关系。
	有时必须在导出类型中添加新的接口元素,这样也就扩展了接口。这个新的类型仍然可以替代基类,但是这种替代并不完美,因为基类无法访问新添加的方法。这种情况我们可以描述为is-like-a(像是一个)关系。新类型具有旧类型的接口,但是它还包含其他方法,所以不能说它们完全相同。例如家里有控制冷气设备的接口,你有一个空调来制冷,但是现在空调坏了,你用一个既能制冷又能制热的热力泵替换了,那么这个热力泵就is-like-a空调,它可以做更多的事。但是房子的控制系统 被设计为只能控制冷气设备,所以它只能和新对象中的制冷部分进行通信。尽管新对象的接口已经被扩展,但是现有系统除了原来接口之外,对其他东西一无所知。
	最后我们会发现制冷系统这个基类不够一般化,应该将其更名为“温度控制系统”,让它可以包括制热功能,这样我们可以套用替代原则了。
2、伴随多态的可互换对象
	在处理类型的层次结构时,经常想把一个对象不把它当作它所属的特定类型来对待,而是将其当作其基类的对象来对待。这使得人们可以编写出不依赖于特定类型的代码。在“几何形”的例子中,方法操作的都是泛型(generic)的星座,而不关系它们是圆形、正方形、三角形还是其他什么尚未定义的形状。所有的几何形状都可以被绘制、擦除和移动,所以这些方法都是直接多对一个几何形对象发送消息,它们不用担心对象将如何处理消息。
	通过导出新的子类型而轻松扩展设计的能力是对改动进行封装的基本方式之一,能极大地改善我们的设计,同时降低软件维护的代价。
	但是使用泛化的对象,编译器不知道应该执行哪一段代码。问题的答案,也是面向对象程序设计的最重要的妙诀:编译器不可能产生传统意义上的函数调用。一个非面向对象编程的编译器产生的函数调用会引起所谓的前期绑定。前期绑定:意味着编译器将产生对一个具体函数名字的调用,而运行时将这个调用解析到将要被执行的代码的绝对地址。然而在OOP中,程序直到运行时才能够确定代码的地址,所以当消息发到一个泛化对象时,必须采用其他的基址。
	为了解决确定代码位置的问题,面向对象程序设计语言使用了后期绑定的概念。当向对象发送消息时,被调用的代码直到运行时才能确定。编译器确保被调用方法的存在,并对调用参数和返回值执行类型检查(无法提供此类保证的语言被称为是弱类型的),但是并不直到将被执行的确切代码。
	为了执行后期绑定,Java使用一小段特殊的代码来替代绝对地址调用。这段代码使用在对象中存储的信息来计算方法体的的地址。这样,根据这一小段代码的内容,每一个对象都可以具有不同的行为表现。当向一个对象发送消息时,该对象就能够直到对这条消息应该做什么。
	在某些语言中,必须明确地声明希望某个方法具备后期绑定属性锁带来的灵活性(C++是使用virtual关键字来实现的)。在这些语言中,方法在默认情况下不是动态绑定的。而在java中,动态绑定是默认行为,不需要添加额外的关键字来实现多态。
	将导出类看作是它的积累的过程被称为向上转型(upcasting)。转型(cast)这个名称的灵感来自于模型铸造的塑模动作;而向上(up)这个词来源于继承图的典型布局方式:通常基类在顶部,而导出类在其下部散开。因此,转型为一个记录就是在继承图中向上移动。
	在Java编译器在编译方法的时候,并不能知道这个方法要处理的确切类型。通常会期望它的编译结果是调用基类的方法,而不是具体的导出类的方法。正是因为多态才使得事情总是能够被正确处理。
3、单根继承结构
	是否所有的类最终都继承自单一基类。在Java(还包括除C++外所有的OOP语言),答案是yes,这个终极基类的名字就是Object。
	在单根继承结构中的所有对象都具有一个共用接口,所以它们归根到底都是相同的基本类型。
4、容器
	通常来说,你不知道在解决某个特定问题时需要多少个对象,或者它们将存活多久, 那么就不可能知道如何存储这些对象。如何才能知道需要多少空间来创建这些对象呢?因为这类信息仅在运行时才能获得,所以不能知道。
	对于面向对象设计的大多数问题,这个解决方案可以为:创建另一种对象类型。这种新的对象类型持有对其他对象的引用。当然, 你可以用在大多数语言中都有的数组类型来实现相同的功能。这个通常被称为容器(也称为集合)。
	List用于存储序列,Map也被称为关联数组中,用来建立对象之间的关联,Set每种对象类型只持有一个。
	ArrayList随机访问元素是一个花费固定事件的操作,但是,对LinkedList来说,随机选取元素需要在列表中移动,这种代价是高昂的,访问越靠近表尾的元素,花费的时间越长。如果想在序列之间插入一个元素,LinkedList的开销比ArrayList小。
5、参数化类型
	为了在向下转型的时候,排除运行时错误,所以创建容器让它知道自己所保存的对象的类型,从而不需要向下转型以及消除犯错误的可能。这种解决方案被称为参数化类型基址。参数化类型就是一个编译器可以自动定制作用于特定类型上的类。
	Java SE5的重大变化之一就是增加了参数化类型,在Java中它称为 泛型。
6、对象的创建和生命期
	对象的数据位于何处?怎样控制对象的生命周期?为了追求最大的执行速度,对象的存储空间和生命周期可以在编写程序时确定。
		6.1、通过将对象置于堆栈(它们有时被称为自动变量或限域变量)或静态存储区域内来实现。这种方式将存储空间分配和施放置于优先考虑的位置,某些情况下这样的控制非常有价值。但是也牺牲了灵活性,因为必须在编写程序时知道对象的确切数量、生命周期和类型。	
		6.2、在被称为堆(heap)的内存池中动态地创建对象,这种方式,直到运行时才知道需要多少对象,以及它们的生命周期和具体类型。因如果需要一个新对象,可以在需要的时空直接在堆中创建。因为存储空间是在运行时被动态管理的,所以需要大量的时间在堆中分配存储空间,可能远远大于在毒地站中创建存储空间的事件。
	Java采用了动态内存分配方式。每当需要新对象时,用new关键字来构建此对象的动态实例。
	Java的垃圾回收器因为所有都系 都是继承自单根基类Object且只能以一种方式创建对象(在堆上创建)这两种特性。所以可以用来处理内存施放问题,知道对象什么时候不再被使用。          
7、Web是什么
	客户/服务器系统的核心思想:系统具有一个中央信息存储池,用来存储某种数据,它通常存在于数据库中,你可以根据需要将它分发给某些人员或或机器集群。客户/服务器概念的关键在于信息存储池的位置集中于中央,这使得它可以被修改,并且这些修改啊将被传播给信息消费者。总之,信息存储池、用于分发信息的软件以及信息与软件所驻留的机器或集群被称为服务器。驻留在用户机器上的软件与服务器进行同学,以获取信息、处理信息,然后将它们显示在被称为客户机的用户机器上。                                  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章