在面向对象的程序语言设计中,多态是继数据抽象和继承之后的第三种基本特征。多态通过分离做什么和怎么做,从另一个角度将接口和实现分离。多态不仅能够改善代码的组织结构和可读性,还能够创建可扩展程序。我们知道对象既可以作为它自己本身的类型使用,也可以作为它的基类型使用。也就是所基类更加通用,甚至可以忘记子对象的类型。
下面首先看一个简单的例子:
[java] view plaincopy
<span style="font-size:14px;">package java_class_thinkink_in_java;
enum Note {
MIDDLE_C, C_SHARP, B_FLAT; // Etc.
}
class Instrument {
public void play(Note n) {
System.out.println("Instrument.play()");
}
}
class Wind extends Instrument {
// Redefine interface method:
public void play(Note n) {
System.out.println("Wind.play() " + n);
}
}
public class Music {
public static void tune(Instrument i) {
// ...
i.play(Note.MIDDLE_C);
}
public static void main(String[] args) {
Instrument inst=new Instrument();
tune(inst);
System.out.println("inst引用对象是:"+inst);
Wind wind = new Wind();
tune(wind);
System.out.println("wind引用对象是:"+wind);
inst=wind;
System.out.println("inst引用对象是:"+inst);
tune(inst);
}
}
</span>
输出的结果是:
[java] view plaincopy
<span style="font-size:14px;">Instrument.play()
inst引用对象是:java_class_thinkink_in_java.Instrument@525483cd
Wind.play() MIDDLE_C
wind引用对象是:java_class_thinkink_in_java.Wind@2a9931f5
inst引用对象是:java_class_thinkink_in_java.Wind@2a9931f5
Wind.play() MIDDLE_C</span>
每一个引用变量都携带者对象的类型信息和地址。Wind@2a9931f5 类名@地址。其实我很好奇向上转型时JVM做了些什么?所以我把主函数改成了:
[java] view plaincopy
<span style="font-size:14px;">public class Music {
public static void main(String[] args) {
Instrument inst=new Wind();
inst.play(Note.MIDDLE_C);
}
}</span>
用jad反汇编了这段代码得到的结果是:
[java] view plaincopy
<span style="font-size:14px;">public class Music
{
public Music()
{
// 0 0:aload_0 //执行 Music的默认构造函数
// 1 1:invokespecial #8 <Method void Object()>
// 2 4:return
}
public static void main(String args[])
{
Instrument inst = new Wind();
// 0 0:new #16 <Class Wind> //创建一个对象,并且其引用进栈
// 1 3:dup //复制栈顶数值,并且复制值进栈
// 2 4:invokespecial #18 <Method void Wind()> //调用超类构造方法、实例初始化方法、私有方法
// 3 7:astore_1
inst.play(Note.MIDDLE_C);
// 4 8:aload_1 //加载对象
// 5 9:getstatic #19 <Field Note Note.MIDDLE_C> //获取指定类的静态域,并将其值压入栈顶
// 6 12:invokevirtual #25 <Method void Instrument.play(Note)> //调用实例方法
// 7 15:return
}
}
</span>
根据便以结果,执行的是 Method void Instrument.play(Note)的方法,可能因为被覆盖了,所以才执行Wind的方法。这时就根据inst 存储引用对象的信息了。这也就是“后期绑定,动态绑定,运行时绑定”,在对象中安置某种类型信息。多态时期更具有通用性或者说兼容性,我们所做的代码修改,不会对程序中其他的不应受到影响的部分产生破坏,换句话说,多态让一个程序员“将改变的事物与未变的事物分离开来”的重要技术。
接口和内部类为我们提供了一种将接口与现实分离的更加结构化的方法。首先我们学习抽象类,它是普通类与接口之间的一种中庸之道。
包含抽象方法(abstract void f())的类叫做抽象类。如果一个类包含一个或多个抽象方法,该类必须被限定为抽象的。如果从一个抽象类继承,并想创建新类的对象,那么就必须为积累中的所有抽象方法提供方法定义。如果不这样做,那么导出类也是抽象类。且必须加上abstract。
interface 关键字使抽象的概念更向前迈进了一步。abstract 关键字允许人们在类中创建一个或多个没有任何定义的方法--提供接口的部分,但是没有提供任何相应的具体实现。interface 关键字产生的一个完全抽象的类,他根本没有提供任何具体的实现。========接口被用来建立类与类之间的协议。我的理解就是接口就是规范或标准,是类与类之间沟通的基础。
接口的完全解耦性:
只要一个方法操作的是类而非接口,那么你就只能使用这个类及其子类。如果你想要将这个方法用于不在此继承结构的某各类,那么你就会触霉头。接口可以很大程度上放宽这种限制,因此,它使得我们可以编写可复用性更好的代码。 |
|