java中的多态 到底怎么使用

Python014

java中的多态 到底怎么使用,第1张

Java中的多态允许父类指针指向子类实例。如:Father obj=new Child()(其中Child是Father的子类)。这样就产生了一个问题——

使用这个父类型的指针访问类的属性或方法时,如果父类和子类都有这个名称的属性或方法,哪一个属性或方法会被调用呢?

最好的办法是实验:

class Father

{

int r

Father()

{

r=4

}

void printname()

{

System.out.println("I’m father")

}

}

class Child extends Father

{

int r

Child()

{

r=5

}

void printname()

{

System.out.println("I’m Child")

}

}

public class Test

{

public static void main(String[] args)

{

Father obj=new Child()

System.out.println(obj.r)

obj.printname()

}

}

结果输出:

4

I’m Child

实验证明。属性会访问父类的。方法分访问子类的。

这就是多态了。

不要以为Father obj=new Child()这条语句一定会让obj.printname()指向Chlid定义的printname()。实际上,如果你把Father类中的printname()方法删除,这个程序将编译失败。因为Father中的并没有定义printname()这个函数。多态是晚绑定*(见本文最后的资料)的,在Father obj=new Child()这个语句中,如果Father中没有printname()这个函数,就不会为obj建立一个用于调用printname()函数的指针。所以调用obj.printname()会出错。如果Father中有这个函数。指向printname()函数的指针会被创建。在调用obj.printname()时,不会出错,而且,因为obj指向的是new Child(),是个Chld类的实例。所以调用obj.printname()时调用了Child类中定义的printname()。这就是方法的动态绑定。

那么,刚才说到把Father类中的printname()方法删掉后,obj将无法调用Child类中的printname(),因为obj.printname()会编译失败。那么如果我就是需要调用要怎么办呢?其实虽然obj是Father类型的,但是它指向的是一个Child类的实例。那么可以将obj强制类型转换为Child。再调用printname()方法就可以了。

在上面程序中,把Father类中的printname()方法整个删掉,再将obj.printname() 改成 ((Child)obj).printname()后,编译成功,结果输出:

4

I’m Child

两次输出的结果都是I’m Child。

那么如何可以运行Child类中的printname()来输出“I’m Father”呢?

其实只需要将Father obj=new Child()改成Father obj=new Father()就可以了,呵呵。另一个办法就是将Child类中定义的printname()整个删掉。为什么这样可以成功呢?自己想想,嘿嘿。最后会有个这样的思考题。

看到这儿你可能早就想问了:

为什么obj.r是4?为什么不是5?

呵呵。其实很简单。Java中的多态仅为方法而言,成员变量还是使用的父类的成员变量。也就是说,因为“Father obj =……”,所以obj是Father类型的,所以obj里面的r是Father里面的r,所以输出obj.r就是4了。

你又想问:

那么5去哪了?new Child()的时候,不是会把5放到Child的r中吗?

实际上5还是有的。只是obj.r是4而已。想访问Child中的r,把5读出来,可以这样写:

((Child)obj).r

就是把obj由Father型强制转换成了Child型。

OK,方法和属性在多态中是什么样的你都清楚了。现在做个题测试一下吧:

这是[email protected]的一道题:

class Base {

int i = 99

public void amethod() {

System.out.println("Base.amethod()")

}

Base() {

amethod()

}

}

public class Derived extends Base {

int i = -1

public static void main(String argv[]) {

Base b = new Derived()

System.out.println(b.i)

b.amethod()

}

public void amethod() {

System.out.println("Derived.amethod()")

}

}

会输出什么?

先想想,再看答案:

答案:

========================

Derived.amethod()

99

Derived.amethod()

========================

讲解:

这个程序的执行过程是这样的:

第一行:Base b=new Derived()

执行这一行语句的过程中,要构造Derived这个类,而它有父类Base,所以先构造Base类。构造Base类的默认构造函数有定义。内容是执行amethod()方法。

实际上,Base类构造方法中的执行amethod(),相当于执行this.amethod(),在这个程序中,就相当于执行b.amethod()。而b是Base类型的,指向了Derived类的实例的指针。所以跟据上面我们的总结,实际上执行的是Derived类的amethod()函数。所以,第一行“Base b=new Derived()”执行完,输出"Derived.amethod()"。

第二行:System.out.println(b.i)

这个很简单,成员变量,不考虑多不多态,只看它定义时前面的类型。这个程序中是Base b,所以b.i就是Base类中的i。输出99

第三行:b.amethod()

调用Derived类中的amethod()方法。

其实这行就是迷惑你的,如果没有这一行。你可能会警觉起来——咦?为什么这儿定义一个amethod()呢?没有地方调用它啊?

有了这行代码,就会使你放松警惕。觉得,啊。定义了这个是用来让b.amethod()调用的。

有一个比较经典的多态实例:

有一个Animal类,它有Cat,和Dog两个子类,在Animal中有个say方法,当Cat调用这个方法的时候输出的是“小猫喵喵喵”,当Dog调用这个方法时,输出的是“小狗汪汪汪”,这就是Java多态的实现。

1、定义一种动物,该类型的动物有叫的属性。

2、分别定义猫,狗,鸟,都继承于该动物,都有叫的属性。

3、分别表示出各个具体小动物的叫声,例如猫的叫声:喵、狗的叫声:汪、鸟的叫声:咻,点是叫声,实现各个具体小动物用的叫声的方法是用的同一个函数名称,就是动物的叫声函数。

多态:

这个案例网上是没有的,属于无忌独创,当时在帮孩子辅导作业,小学科学,里面有一点内容是关于人的牙齿,说牙齿分为:门齿、犬齿、臼齿。

问闺女,为什么这么分呢?闺女说牙齿虽然都是用来咬食物,但是食物种类很多,咬碎需要的工具也不一样,门齿用来切割食物,如:苹果、梨;犬齿用来撕碎食物。

如肉类;臼齿用来磨碎食物,如犬齿撕碎的肉类,就需要再用臼齿来磨碎,然后送到食道,胃,小肠,大肠进行消化。我恍然大悟,这不就是Java面向对象里的多态吗?多完美啊。

这也很好说明了为什么会有多态出现,因为生活中就存在太多这种例子,所以需要多态来匹配解决。

多态性是指允许不同子类型的对象对同一消息作出不同的响应。简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情。多态性分为编译时的多态性和运行时的多态性。

如果将对象的方法视为对象向外界提供的服务,那么运行时的多态性可以解释为:当A系统访问B系统提供的服务时,B系统有多种提供服务的方式,但一切对A系统来说都是透明的(就像电动剃须刀是A系统,它的供电系统是B系统,B系统可以使用电池供电或者用交流电,甚至还有可能是太阳能,A系统只会通过B类对象调用供电的方法,但并不知道供电系统的底层实现是什么,究竟通过何种方式获得了动力)。

方法重载(overload)实现的是编译时的多态性(也称为前绑定),而方法重写(override)实现的是运行时的多态性(也称为后绑定)。