码迷,mamicode.com
首页 > 编程语言 > 详细

java多态总结

时间:2015-03-20 18:29:13      阅读:149      评论:0      收藏:0      [点我收藏+]

标签:

 

/*
一、多态:多态是指一个事物的多种存在状态(一个事物的多种体现形态或者一个事物的多种表现形态)

1、多态的体现:
多态在代码中的体现为:父类引用指向了子类对象
即 父类 a = new 子类();

2、多态的前提:
1,类与类之间必须存在关系,要么是继承,要么是实现(类实现接口,接口相当于父类,该类相当于子类)
2,子类要覆盖父类的方法,即要存在覆盖(覆写)。这样就会使得,用父类引用调用父类和子类共有的
方法(即子类所覆盖的方法)时,会执行(调用)子类定义里的方法(覆盖父类方法的子类方法)。为什么会
出现这样的现象呢?
原因是,该事物的本质是子类对象,只不过这一时刻,其扮演的父类的对象的角色。它尽可能演好父类,但
他毕竟是子类,你可以拿着父类的引用,将其当成父类使用,但是,你所访问的都是子类对象的内容。包括
父类中的一些方法,全部都是子类的方法。

3、多态的好处:
1,极大的提高了程序的扩展性,使得在升级程序时,变得容易

4、多态的弊端:
1,使用多态时,即用父类引用指向子类对象时,不可以用父类引用访问子类中特有的方法。只能访问父类中已有的
方法。
5、转型:
向上转型:引用变量的向上转型是自动完成的,在子类对象赋给父类引用时,会将子类对象的类型转换成父类类型。
例如:Fu f = new Zi();
数值类型的变量的向上转型也是自动完成的,例如 : byte b = 3 ; int a = b ;

向下转型:引用变量的向下转型是强制完成的,指向子类对象的父类引用可以通过强制类型转换,由父类类型转换成
子类类型。
例如 Zi z = (Zi)f; // Fu f = new Zi();
数值类型的变量的向下转型也是强制完成的,例如: int a = 3 ; byte b = (byte)a;

6、编译器对于转型的检查:
6.1、 对于引用类型的向下转型,编译器无法判断对错,只能在运行时判断正确与否。因为
一个父类可能有多个子类,父类引用F可能指向子类A的对象,也可能指向子类B的对象,只要
指向子类对象,这一句就没有错。

F f = new A() ; //编译通过,因为A是F的子类

而一个父类引用被强制转换成子类类型A,或者子类类型B,都是正确的。所以这一句强制转换
也没有错。
F f = new A();
B b = (B)f ; //编译通过,因为f是父类F类型的引用,B是F的子类。但是运行时就会
//报错,因为f本质上是子类A的对象,其无法转成子类B的对象,它扮演
//不了子类B的对象,因为其不具备子类B中的某些方法,它能扮演父类F
//是因为其具备所有父类的功能,能成功扮演父类F的对象。

6.2 、 但对于引用类型的向上转型,编译器是可以检查出正确与否的。标准就是,子类类型转父类
类型没问题。

7、多态的本质是:一个类的对象在多种存在状态间切换。自始至终不变化的是这个对象,变化的是该对象的对外提供的
访问类型,或者说变化的是该对象对外提供的存在状态。

7.1、一个生动的例子:
一个父亲:老王,一个儿子:小王。“老王”,“小王”都是名字。但是这个名字
表示出了哪个是父亲,哪个是儿子,父亲是一种类型,代表的是老一代,儿子是另一种类型,代表
的是新一代。即
儿子继承父亲。
class 父亲
{
void 讲课()
{
s.o.p(工商管理);
}
}
class 儿子 extends 父亲
{
void 讲课() //覆盖了父类(父亲)中的方法“讲课”
{
s.o.p(Java编程);
}
void 看电影()
{
s.o.p(看电影);
}
}
父亲 老王 = new 父亲();
儿子 小王 = new 儿子();

这时候,认识父亲的老李来了,老李老找父亲老王去讲课,讲工商管理,父亲不在,儿子小王
在家,这时,儿子小王化妆成父亲老王(将子类对象赋值给父类引用),开门见到老李,
老李拉着老王(小王化妆的)就走了,到了教室里,老李让老王开始讲课吧(老李调用老王的
讲课方法,即“老王.讲课()”),老王(儿子小王)上了讲台就开始讲课,一开口讲的是
“Java编程”,为什么呢?因为老王是小王扮演的,小王不会讲工商管理,小王只会讲Java编程,
小王讲完后回家。
这时候,小李来找小王,小王还没有卸妆,开门后,小李说“王叔叔,我找小王看电影”,老王
(小王扮演的)说“等会啊,我去叫他”,老王到了屋里就开始卸妆(将指向自己的子类对象的
父类引用强制转换成子类类型,赋给子类引用,即 "儿子 小王 = (儿子)老王; "),然后出来
和小李一起去看电影了(小李调用了小王的看电影方法,"小王.看电影()")。
为什么老王不能去看电影,直接让小李调用老王.看电影不行么?不行!因为老王(父类)没有
这个方法,这是子类所特有的,调用就会出错,找不到该方法。

分别对应这样的两个方法:
public void static main(String[] args)
{
父亲 老王 = new 儿子();//只有儿子在家,儿子扮演老王

老李找老王讲课(老王);

儿子 小王 = (儿子)老王; //卸妆,儿子小王变回儿子状态,做回自己

小李找小王看电影(小王)
}
public static void 老李找老王讲课(父亲 讲课人)
{
讲课人.讲课();
}
public static void 小李找小王看电影(儿子 看电影的人)
{
看电影的人.看电影();
}


8、总结:
1、对于对象而言,即对应通过对象可以访问的类成员。
任何一个名称(标识符)都必须有一个类型,类型就确定了该名称所能对外提供的功能。名称
是外界使用该对象的一个称谓(句柄,标志),通过该名称(其代表一个引用变量的值即地址)
可以拿到该对象,通过该对象可以使用该对象所具有的功能,而该对象通过该名称对外提供的
功能由该名称的类型所确定。

类型 名称 对象
类 引用变量名 new 类();

 

2、对于类名而言,即对应只需要类名即可访问的类成员。
名称就是类名,既是对外提供的称谓(句柄、标志),也表征了类型,从而也就确定其对外所
能提供的功能。通过该名称就可以使用这种情况下该类型所提供的功能。类名的值就是该类在
方法区中的首地址。

类型(名称)
类名


3、对于非引用类型的变量而言,即对应于数值类型的变量
名称必须有一个类型,类型确定了该变量对外所能提供的功能,名称是该变量的称谓(代表)
,通过该名称就可以使用变量,从而使用变量所提供的功能(一般就是存储功能)。

类型 名称 值
类型 变量名 值


4、而上述三种情况的名称分别对应如下:
三种情况的名称本质上都是对其内的值的引用。出现名称的地方就相当于将其名称所
所代表的变量的值写在那。在代码中名称和值之间是一一映射的。
所以,看到变量名称就将其对应成其所代表的值(内存空间的数据)或者内存空间。


变量的名称在代码执行时,相当于变量的值。这一过程是这样做到的:
首先在编译时,编译器会将变量名称编译成一个逻辑地址,该逻辑地址是该变量的逻辑地址的
首地址,在执行代码,分配内存的时候才会在栈内存(或方法区)中分配物理地址,当使用该变量
名时,就会先通过逻辑地址和物理地址之间的映射(即地址的虚实变换),找到该变量的物理地址
的首地址,然后通过该变量的类型(即名称前的类型),确定该内存地址空间的长度(大小),
从而正确的取出该内存空间中的二进制数据,并根据变量的类型正确解析出来值。至此,就完成了
变量名到值转换过程,该值就是变量的值,就是该名称所代表的变量的值。然后拿着该值进行操作
,完成语句的执行。
例1: int a = 3;
int b = a*4;
现在内存中开分配空间,存储数字3的二进制,执行第二句时,看到名称(标志符)a会将
变量a的值3取出来,进行和4的乘法操作,然后再赋值到b中。
第二句对应的汇编语句至少有4条:
将a内存空间中的值取出来放入cpu寄存器A,
将4从内存中取出来放入cpu寄存器B,
将寄存器A和B进行乘法运算,并将结果放到A中
再将A中的数据存放到b所代表的内存空间中。

例2: Animal a = new Animal();
a.eat();
a.age = 3;
先在栈内存中分配引用变量a,堆内存中分配对象内存。第二句,看到名称名称a会将引用
变量a中的值,即对象在堆内存中的首地址,取出来(取出过程如上面分析)。然后用该
该值进行“.”运算,找到堆内存中的该对象,然后访问的是eat()成员,所以查找该对象
的成员方法表,在方法区中找到该方法的代码,执行该函数(在栈内存中开辟该方法的
空间,用来存储该方法内的局部变量)。
第三句执行时,看到名称a取出其值,即对象的首地址,通过.运算,找到该对象,然后
访问的是age成员,通过成员age的类型,结合对象的首地址,计算出age在堆内存中的
首地址,然后找到该空间,将3存入该空间。即时是a.age中的age也是代表其所代表的空间
中的值,其值原来为0,现在被赋值3。
例3: Animal.sleep();

先加载类Animal进入内存中的方法区,然后通过Animal取出Animal的值,即其所代表的
类Animal的内存首地址。进行“.”运算找到类Animal,访问成员方法sleep()。

这里讲的值本质上是一段内存空间中的数据(取值时)或一段内存空间(赋值时)。
9、instanceof 关键字,是一个二元操作符,使用方法:

引用变量名称 instanceof 类名

用来判断引用变量名称所代表的引用变量指向的对象是否是一个“类名”所表示的类的实例
即用来判断引用变量所指向的对象(实例)是不是(可以当成、看成、转成)某一个类的对象(实例)。
若是,则返回true,否则返回false。

例如:
Animal a = new Cat();//向上转型,自动进行。类型提升
if(a instanceof Cat) //判断一下引用变量a所指向的对象是不是一个Cat类的对象(实例)
{
Cat c = (Cat)a; //向下转型,强制进行,将对象还原成原本的类型。
c.catchMouse(); //当成一个子类对象(猫类对象)进行使用,调用子类(猫)的
//特有方法。
}
if(a instanceof Dog)
{
Dog d =(Dog)a;
d.watchHome();
}
总结: 所谓的转型就是指转换类型,转换对象扮演的角色,转换对象存在的状态。使用对象的另一种
形态,或者另一种角色,或者对象的另一种存在形式,就是将子类对象当成祖先类对象来使用

一个对象,可以扮演很多角色(可以拥有很多种存在状态,可以有很多种体现形态),可以
当成很多种类型的对象来使用,一个对象可以当成本类,父类,祖父类,所有祖先类的对象
来使用。可以这样做的原因是因为该对象继承了所有祖先类,拥有祖先类的所有内容,一个
最先类对象有的东西,它全有,它可以完美的扮演祖先对象,完美的当成祖先对象使用,
而不会出现任何差错。
(有人可能觉得它和祖先类不一样,对于覆盖的方法而言,他们执行
的不是同一个方法,方法的内容不同,所以不能当成祖先类的对象来使用,这样思考是错误
的,因为类的封装(函数)特性本身就是对外屏蔽了方法的实现过程,只暴露出方法的
调用接口,只暴露出功能定义,隐藏功能内容,所以对于外界而言,子类对象拥有一样的
功能定义,那么就可以像调用祖先类对象那样使用子类对象,就可以将子类对象当成祖先类
对象使用)

向上转型,之所以是自动转换,这个“自动转换”不过是大家起的一个名字,以示和向下转型
的强制转换相区分,不存在什么“自动”“不自动”“强制”“不强制”,本质上是指不需要标识出
要转换的目标类型,是因为将一个子类的引用
赋值给父类的引用是否是合法的,只需要根据赋值号=两端的类型(值类型,变量类型
)就可以判断出这样赋值正确与否。值类型是子类类型,变量类型是父类类型,没问题,
完全正确。子类对象可以当成父类对象来去使用。

向下转型,之所以要强制转换,这个“强制转换”不过是大家起的一个名字,以示和向上转型
的自动转换区分。不存在什么“强制”“不强制”“自动”“不自动”,本质上是指需要标识出要转
换的目标类型,因为将一个父类引用赋值给一个子类引用是否是合法的,只通过赋值号=
两端的类型(值类型,变量类型)来检查,不能判断出来正确与否,必须要通过查看赋值
号右侧值类型所代表的对象能否当成左侧的类型的对象来去使用。可以则赋值是合法的,
否则非法,出错。所以Cat c = (Cat)a ;中a前面的(cat)就是用来让JVM检查a指向的对象
是否可以当成Cat对象来使用,若可以,则赋值成功,否则出错。而语法规定必须要加上
(Cat)去提醒JVM检查,即时是已经用instanceof判断过了,也要加上(Cat)。一个父类有
多个子类决定了这一点。


new 类名();//表达式返回的是对象的首地址(即对象的句柄,对象的引用)。这个地址的
//数据类型是对象的类的类型。同一个数据类型不同,解释不同。

*/


abstract class Animal
{
abstract void eat();
}
class Cat extends Animal
{
public void eat()
{
System.out.println("吃鱼");
}
public void catchMouse()
{
System.out.println("抓老鼠");
}
}
class Dog extends Animal
{
public void eat()
{
System.out.println("吃骨头");
}
public void watchHome()
{
System.out.println("看家");
}
}
class Pig extends Animal
{
public void eat()
{
System.out.println("吃饲料");
}
public void run()
{
System.out.println("猪跑步!");
}
}

class Polymorphism
{
public static void main(String[] args)
{
Animal a = new Cat();
feedAnimal(a);
feedAnimal(new Dog());
feedAnimal(new Pig());
}
public static void feedAnimal(Animal a)//Animal a = new Cat();多态的使用
{
a.eat();
if(a instanceof Cat)
{
Cat c = (Cat)a;
c.catchMouse();
}
if(a instanceof Dog)
{
Dog d = (Dog)a;
d.watchHome();
}
if(a instanceof Pig)
{
Pig p = (Pig)a;
p.run();
}
}
}

java多态总结

标签:

原文地址:http://www.cnblogs.com/wllbelief-win/p/4354062.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!