think in java回顾整理之多形性

“多形性”(Polymorphism)从另一个角度将接口从具体的实施细节中分离出来,亦即实现了“是什么”与“怎样做”两个模块的分离。

可将一个对象作为它自己的类型使用,或者作为它的基础类型的一个对象使用。取得一个对象句柄,并将其作为基础类型句柄使用的行为就叫作“上溯造型”——因为继承树的画法是基础类位于最上方。

将一个方法调用同一个方法主体连接到一起就称为“绑定”(Binding)。若在程序运行以前执行绑定(由编译器和链接程序,如果有的话),就叫作“早期绑定”。

“后期绑定”,它意味着绑定在运行期间进行,以对象的类型为基础。后期绑定也叫作“动态绑定”或“运行期绑定”。

Java中绑定的所有方法都采用后期绑定技术,除非一个方法已被声明成final。这意味着我们通常不必决定是否应进行后期绑定——它是自动发生的。

把一个方法声明成final呢?它能防止其他人覆盖那个方法,它可有效地“关闭”动态绑定,或者告诉编译器不需要进行动态绑定。

将一条消息发给一个对象,让对象自行判断要做什么事情。

多形性是一种至关重要的技术,它允许程序员“将发生改变的东西同没有发生改变的东西区分开”。

“过载”是指同一样东西在不同的地方具有多种含义;而“覆盖”是指它随时随地都只有一种含义,只是原先的含义完全被后来的含义取代了。

“抽象方法”:它属于一种不完整的方法,只含有一个声明,没有方法主体。

如果一个类里包含了一个或多个抽象方法,类就必须指定成abstract(抽象)。

如果从一个抽象类继承,而且想生成新类型的一个对象,就必须为基础类中的所有抽象方法提供方法定义。如果不这样做(完全可以选择不做),则衍生类也会是抽象的,而且编译器会强迫我们用abstract关键字标志那个类的“抽象”本质。

“interface”(接口)关键字使抽象的概念更深入了一层。我们可将其想象为一个“纯”抽象类。

接口也包含了基本数据类型的数据成员,但它们都默认为static和final。接口只提供一种形式,并不提供实施的细节。

一个接口中的方法声明明确定义为“public”。但即便不明确定义,它们也会默认为public。
所以在实现一个接口的时候,来自接口的方法必须定义成public。否则的话,它们会默认为“友好的”,而且会限制我们在继承过程中对一个方法的访问——Java编译器不允许我们那样做。
抽象类不存在此类问题,抽象类中不允许定义一个私有的抽象方法,最小范围为friendly的,在实现类中可以扩大访问权限,这也是我们所希望的,编译器会自动检查。

没有与存储空间与“接口”关联在一起——所以没有任何办法可以防止多个接口合并到一起。

我们可根据需要使用多个接口,而且每个接口都会成为一个独立的类型,可对其进行上溯造型。

到底应该使用一个接口还是一个抽象类呢?若使用接口,我们可以同时获得抽象类以及接口的好处。所以假如想创建的基础类没有任何方法定义或者成员变量,那么无论如何都愿意使用接口,而不要选择抽象类。只有在必须使用方法定义或者成员变量的时候,才应考虑采用抽象类。

利用继承技术,可方便地为一个接口添加新的方法声明,也可以将几个接口合并成一个新接口。

由于置入一个接口的所有字段都自动具有static和final属性,所以接口是对常数值进行分组的一个好工具。

接口中定义的字段会自动具有static和final属性。它们不能是“空白final”,但可初始化成非常数表达式。字段并不是接口的一部分,而是保存于那个接口的static存储区域中。

将一个类定义置入另一个类定义中。这就叫作“内部类”。内部类对我们非常有用,因为利用它可对那些逻辑上相互联系的类进行分组,并可控制一个类在另一个类里的“可见性”。

一个外部类拥有一个特殊的方法,它会返回指向一个内部类的句柄。

若想在除外部类非static方法内部之外的任何地方生成内部类的一个对象,必须将那个对象的类型设为“外部类名.内部类名”,就象main()中展示的那样。

当我们准备上溯造型到一个基础类(特别是到一个接口)的时候,内部类就开始发挥其关键作用(从用于实现的对象生成一个接口句柄具有与上溯造型至一个基础类相同的效果)。
这是由于内部类随后可完全进入不可见或不可用状态——对任何人都将如此。

内部类:
(1) 在一个方法内定义的类
(2) 在方法的一个作用域内定义的类
(3) 一个匿名类,用于实现一个接口
(4) 一个匿名类,用于扩展拥有非默认构建器的一个类
(5) 一个匿名类,用于执行字段初始化
(6) 一个匿名类,通过实例初始化进行构建(匿名内部类不可拥有构建器)

在一个方法的作用域(而不是另一个类的作用域)中创建一个完整的类。
在任意作用域内嵌套一个内部类。

匿名类不能拥有一个构建器。

若想对匿名内部类的一个对象进行某种形式的初始化,此时会出现什么情况呢?由于它是匿名的,没有名字赋给构建器,所以我们不能拥有一个构建器。然而,我们可在定义自己的字段时进行初始化。

若试图定义一个匿名内部类,并想使用在匿名内部类外部定义的一个对象,则编译器要求外部对象为final属性。如果忘记这样做,就会得到一条编译期出错提示。

假如需要采取一些类似于构建器的行动,又应怎样操作呢?
一个实例初始化模块就是一个匿名内部类的构建器。当然,它的功能是有限的;我们不能对实例初始化模块进行过载处理,所以只能拥有这些构建器的其中一个。

一个内部类可以访问封装类的成员。这是如何实现的呢?内部类必须拥有对封装类的特定对象的一个引用,而封装类的作用就是创建这个内部类。随后,当我们引用封装类的一个成员时,就利用那个(隐藏)的引用来选择那个成员。

内部类的一个对象只能与封装类的一个对象联合创建。在这个创建过程中,要求对封装类对象的句柄进行初始化。若不能访问那个句柄,编译器就会报错。

内部类的对象默认持有创建它的那个封装类的一个对象的句柄。然而,假如我们说一个内部类是static的,这种说法却是不成立的。
static内部类意味着:
(1) 为创建一个static内部类的对象,我们不需要一个外部类对象。
(2) 不能从static内部类的一个对象中访问一个外部类对象。
但在存在一些限制:由于static成员只能位于一个类的外部级别,所以内部类不可拥有static数据或static内部类。


static内部类可以成为接口的一部分。
由于类是“静态”的,所以它不会违反接口的规则——static内部类只位于接口的命名空间内部。

必须利用外部类的一个对象生成内部类的一个对象。除非已拥有外部类的一个对象,否则不可能创建内部类的一个对象。这是由于内部类的对象已同创建它的外部类的对象“默默”地连接到一起。然而,如果生成一个static内部类,就不需要指向外部类对象的一个句柄。

内部类可以覆盖吗?可以。但是很特殊,需要双重限定。

内部类的class文件或类的名字遵守一种严格的形式:先是封装类的名字,再跟随一个$,再跟随内部类的名字。
如果内部类是匿名的,那么编译器会简单地生成数字,把它们作为内部类标识符使用。
若内部类嵌套于其他内部类中,则它们的名字简单地追加在一个$以及外部类标识符的后面。


为什么要用内部类:控制框架。
“控制框架”属于应用程序框架的一种特殊类型,受到对事件响应的需要的支配;主要用来响应事件的一个系统叫作“由事件驱动的系统”。

(1) 在单独一个类里表达一个控制框架应用的全部实施细节,从而完整地封装与那个实施有关的所有东西。内部类用于表达多种不同类型的action(),它们用于解决实际的问题。除此以外,后续的例子使用了private内部类,所以实施细节会完全隐藏起来,可以安全地修改。
(2) 内部类使我们具体的实施变得更加巧妙,因为能方便地访问外部类的任何成员。若不具备这种能力,代码看起来就可能没那么使人舒服,最后不得不寻找其他方法解决。


构建器负有一项特殊任务:检查对象是否得到了正确的构建。

构建器的调用遵照下面的顺序:
(1) 调用基础类构建器。这个步骤会不断重复下去,首先得到构建的是分级结构的根部,然后是下一个衍生类,等等。直到抵达最深一层的衍生类。
(2) 按声明顺序调用成员初始化模块。
(3) 调用衍生构建器的主体。

初始化的实际过程是这样的:
(1) 在采取其他任何操作之前,为对象分配的存储空间初始化成二进制零。
(2) 就象前面叙述的那样,调用基础类构建器。
(3) 按照原先声明的顺序调用成员初始化代码。
(4) 调用衍生类构建器的主体。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值