引论 :
在处理类型的层次结构时,你经常想把一个对象不要当作它所属的特定类型来对待,而是将其当作其基类的对象来对待。这使得你可以编写出不依赖于特定类型的代码。在shape的例子中,方法都是用来操作泛化(generic)形状的,不管它们是圆形、正方形、三角形还是其他什么尚未定义的形状。所有的几何形状都可以被绘制、被擦除、被移动,所以这些方法都是直接对一个shape对象发送消息,并不用担心这个对象如何处理该消息。
这样的代码是不会受添加新类型的影响的,而且添加新类型是扩展一个面向对象程序已处理新情况的最常用方式。例如,你可以从shape中导出一个新的子类型pentagon(无边形),而并不需要修改处理泛化几何形状的方法。通过导出新的子类型而轻松扩展设计的能力是封装改动的基本方式之一。这种能力可以极大地改善我们的设计,同时也降低了软件维护的代价。
但是,在试图将导出类型的对象当作他们的泛化基类对象来看待时(把圆形看作是几何形状,把自行车看作是交通工具,把鸬鹚看作是鸟等等),仍然存在一个问题。如果某个方法是要泛化几何形状绘制自己,泛化交通工具前进,或者是泛化的鸟类移动,那么编译器在编译时是不可能知道应该执行哪一段代码的。这就是关键所在:当发送这样的消息时,程序员并不想知道哪一段代码将被执行;绘图(draw)方法可以被等同地应用于圆形、正方形、三角形之上,而对象会依据自身的具体类型来执行恰当的代码。如果你不需要知道哪一段代码会被执行,那么当你添加新的子类型时,不需要更改方法调用的代码,就能够执行不同的代码。因此,编译器无法精确地了解哪一段代码将会被执行,那么它该怎么办呢?例如,在下面的图中,BirdController对象仅仅处理泛化的Bird对象,而不了解它们的确切类型。从BirdController的角度看,这么做非常方便,因为不需要编写特别的代码来判定要处理的Bird对象的确切类型或是Bird对象的行为。当move()方法被调用时,即便忽略Bird的具体类型,也会产生正确的行为(鹅跑、飞或游泳,企鹅跑或游泳),那么,这又是如何发生的呢?
这个问题的答案,也是面向对象程序设计的最重要的妙诀:编译器不可能产生传统意义上的函数调用(function call)。一个非面向对象(non-OOP)编译器产生的函数调用会引起所谓的“前期绑定(early binding)”,这个术语你可能以前从未听说过,因为你从未想过函数调用的其他方式。这么做意味着编译器将产生对一个具体函数名字的调用,而链接器(linker)将这个调用解析到将要被执行代码的绝对地址(absolute address)。在OOP中,程序直到运行时刻才能够确定代码的地址,所以当消息发送到一个泛化对象时,必须采用其他的机制。
为了解决这个问题,面向对象程序设计语言使用了“后期绑定(late binding)”的概念。当你向对象发送消息时,被调用的代码直到运行时刻才能被确定。编译器确保被调用方法存在,并对调用参数(argument)和返回值(return value)执行类型检查(无法提供此类保证的语言被称为是弱类型的(weakly typed)),但是并不知道将会被执行的确切代码。
为了执行后期绑定,Java使用一小段特殊的代码来替代绝对地址调用。这段代码使用在对象中存储的信息来计算方法体的地址(这个过程将在第7章中详述)。这样,根据这一小段代码的内容,每一个对象都可以具有不同的行为表现。当你向一个对象发送消息时,该对象就能够知道对这条消息应该做些什么。
在某些语言中,你必须明确地声明希望某个方法具备后期绑定属性所带来的灵活性(C++是使用virtual关键字来实现的)。在这些语言中,方法在缺省情况下不是动态绑定的。而在Java中,动态绑定是缺省行为,你不需要添加额外的关键字来实现多态(polymorphism)。
提问:
为什么要使用 spring?
解释一下什么是 aop?
解释一下什么是 ioc?
spring 有哪些主要模块?
spring 常用的注入方式有哪些?
spring 中的 bean 是线程安全的吗?
spring 支持几种 bean 的作用域?
spring 自动装配 bean 有哪些方式?
spring 事务实现方式有哪些?
说一下 spring 的事务隔离?
说一下 spring mvc 运行流程?
spring mvc 有哪些组件?
@RequestMapping 的作用是什么?
@Autowired 的作用是什么?