java多态性详解父类引用子类对象-

发布时间:   来源:文档文库   
字号:
Java 多态性详解——父类引用子类对象
核心提示:面向对象编程有三个特征,即封装、继承和 多态。封装隐藏了类的内部实现机制,从而可以在不影响使 用者的前提下改变类的内部结构,同时保护了数据。面向对 象编程有三个特征,即封装、继承和多态。封装隐藏了类的 内部实现机制,从而可以在不影响使用者的前提下改变类的 内部结构,同时保护了数据。继承是为了重用父类代码,同 时为实现多态性作准备。那么什么是多态呢?方法的重写、 重载与动态连接构成多态性。 Java 之所以引入多态的概念, 原因之一是它在类的继承问题上和 C++ 不同,后者允许多继 承,这确实给其带来的非常强大的功能,但是复杂的继承关 系也给 C++ 开发者带来了更大的麻烦, 为了规避风险, Java 只允许单继承,派生类与基类间有 IS-A 的关系(即“猫” is a “动物”。这样做虽然保证了继承关系的简单明了,但是势必
在功能上有很大的限制, 所以, Java 引入了多态性的概念以 弥补这点的不足,此外,抽象类和接口也是解决单继承规定 限制的重要手段。 同时,多态也是面向对象编程的精髓所在。 要理解多态性,首先要知道什么是“向上转型”。我定义了一 个子类 Cat ,它继承了 Animal 类,那么后者就是前者是父类。 我可以通过 Cat c = new Cat(; 实例化一个 Cat 的对象, 这个 不难理解。但当我这样定义时: Animal a = new Cat(;
表什么意思呢?很简单,它表示我定义了一个 Animal 类型 的引用,指向新建的 Cat 类型的对象。由于 Cat 是继承自它 的父类 Animal
所以 Animal 类型的引用是可以指向 Cat 型的对象的。那么这样做有什么意义呢?因为子类是对父类 的一个改进和扩充,所以一般子类在功能上较父类更强大, 属性较父类更独特,定义一个父类类型的引用指向一个子类 的对象既可以使用子类强大的功能,又可以抽取父类的共性。 所以,父类类型的引用可以调用父类中定义的所有属性和方 法,而对于子类中定义而父类中没有的方法,它是无可奈何 的;同时,父类中的一个方法只有在在父类中定义而在子类 中没有重写的情况下,才可以被父类类型的引用调用;对于 父类中定义的方法,如果子类中重写了该方法,那么父类类 型的引用将会调用子类中的这个方法,这就是动态连接。看 下面这段程序:
class Father{public void func1({func2(;}// 这是父类中的 func2( 方法,因为下面的子类中重写了该方法 //所以在父类类型的引用中调用时,这个方法将不再有 // 取而代之的是将调用子类中重写的 func2( 方法
public void func2({System.out.println("AAA";}}class Child extends Father{//func1(int i 是对 func1( 方法的一个重载 //由于在父
类中没有定义这个方法,所以它不能被父类类型的引用调用
//所以在下面的 main 方法中 child.func1(68 是不对的 public void func1(int i{System.out.println("BBB";}//func2( 重写
父类 Father 中的 func2( 方法 //如果父类类型的引用中调用了
func2( 方法,那么必然是子类中重写的这个方法 public void func2({System.out.println("CCC";}}public class PolymorphismTest {public static void main(String[] args {Father child = new Child(;child.func1(;// 打印结果将会是
什么? }}上面的程序是个很典型的多态的例子。子类
Child 继承了父类 Father ,并重载了父类的 func1( 方法,重写了 父类的
func2( 方法。重载后的 func1(int i func1( 不再是
同一个方法,由于父类中没有 func1(int i ,那么,父类类型 的引用
child 就不能调用 func1(int i 方法。而子类重写了 func2( 法,那么父类类型的引用 child 在调用该方法时将 会调用子类中重写 func2( 。那么该程序将会打印出什么样 的结果呢?很显然,应该是“
CCC。对于多态,可以总结它
为:一、使用父类类型的引用指向子类的对象;二、该引用 只能调用父类中定义的方法和变量;三、如果子类中重写了 父类中的一个方法,那么在调用这个方法的时候,将会调用 子类中的这个方法; (动态连接、动态调用四、变量不能 被重写(覆盖 ,”重写“的概念只针对方法,如果在子类中” 重写“了父类中的变量,那么在编译时会报错。 ****************************************************************** **********************************************************多态详

1 接口 实现接口并覆盖接口中同一方法的几不同的类 体现的 2 继承父类并覆盖父类中同一方法的几个 不同子类实现的 .一、基本概念多态性: 发送消息给某个对象, 让该对象自行决定响应何种行为。通过将子类对象引用赋值 给超类对象引用变量来实现动态方法调用。 java 的这种机制 遵循一个原则:当超类对象引用变量引用子类对象时,被引 用对象的类型而不是引用变量的类型决定了调用谁的成员 法,但是这个被调用的方法必须是在超类中定义过的,也 就是说被子类覆盖的方法。 1. 如果 a 是类 A 的一个引用, 那么,a可以指向类A的一个实例或者说指向类 A的一个子 类。2•如果a是接口 A的一个引用,那么,a必须指向实现 了接口 A 的一个类的实例。二、 Java 态性实现机制 SUN 目前的 JVM 实现机制,类实例的引用就是指向一个句柄 (handle 的指针,这个句柄是一对指针:一个指针指向一 张表格,实际上这个表格也有两个指针(一个指针指向一个 包含了对象的方法表,另外一个指向类对象,表明该对象所 属的类型;另一个指针指向一块从 java 堆中为分配出来内 存空间。三、总结 1、通过将子类对象引用赋值给超类对象 引用变量来实现动态方法调用。
DerivedC c2=new DerivedC(;BaseClass a1= c2; //BaseClass 基类, DerivedC 继承自 BaseClass 的子类 a1.play(; //play( BaseClass
DerivedC 中均有定义, 即子类覆写了该方法分 析: * 为什么子类的类型的对象实例可以覆给超类引用?自 动实现向上转型。通过该语句,编译器自动将子类实例向上 移动,成为通用类型 BaseClass * a.play( 将执行子类还是
父类定义的方法?子类的。在运行时期,将
根据 a 这个对象 引用实际的类型来获取对应的方法。所以才有多态性。一个 基类的对象引用,被赋予不同的子类对象引用,执行该方法 时,将表现出不同的行为。在 a1=c2 的时候,仍然是存在两 个句柄, a1
c2 ,但是 a1 c2 拥有同一块数据内存块和 不同的函数表。 2、不能把父类对象引用赋给子类对象引用 变量
BaseClass a2=new BaseClass(;DerivedC c1=a2;// 错在 java 里面,向上转型是自动进行的 ,但是向下转型却不 是,需要我们自己定义强制进行。
c1=(DerivedCa2; 进行强 制转化 ,也就是向下转型 .3 、记住一个很简单又很复杂的规则, 一个类型引用只能引用引用类型自身含有的方法和变量。你 可能说这个规则不对的,因为父类引用指向子类对象的时候, 最后执行的是子类的方法的。其实这并不矛盾,那是因为采 用了后期绑定,动态运行的时候又根据型别去调用了子类的 方法。而假若子类的这个方法在父类中并没有定义,则会出 错。例如,DerivedC类在继承BaseClass中定义的函数外, 还增加了几个函数(例如 myFun( 析:当你使用父类引 用指向子类的时候,其实 jvm 已经使用了编译器产生的类型 信息调整转换了。这里你可以这样理解,相当于把不是父类 中含有的函数从虚拟函数表中设置为不可见的。注意有可能 虚拟函数表中有些函数地址由于在子类中已经被改写了,所 以对象虚拟函数表中虚拟函数项目地址已经被设置为子类 中完成的方法体的地址了。 4Java C++ 多态性的比较 jvm 关于多态性支持解决方法是和 c++ 中几乎一样的,只是 c++ 中编译器很多是把类型信息和虚拟函数信息都放在一个 拟函数表中, 但是利用某种技术来区别。 Java 把类型信息和
函数
信息分开放。 Java 中在继承以后, 子类会重新设置自己 的虚拟函数表,这个虚拟函数表中的项目有由两部分组成。 从父类继承的虚拟函数和子类自己的虚拟函数。虚拟函数调 用是经过虚拟函数表间接调用的,所以才得以实现多态的。 Java 的所有函数, 除了被声明为 final 的,都是用后期绑定。 . 1 个行为 ,不同的对象 ,他们具体体现出来的方式不一 ,比如 : 方法重载 overloading 以及 方法重写 ( overrideclass Human{void run({ 输出 人在跑
}}class Man extends Human{void run({ 输出 男人在跑 }} 这个时候 , 同是跑,不同的对象 ,不一样 (这个是方法覆盖的例子
class Test{void out(String str{ 输出 str}void out(int i{ 输出 i}}
个例子是方法重载 ,方法名相同 ,参数表不同 ok, 明白了这些 还不够 , 用人在跑举例 Human ahuman=new Man(; 这样 我等于实例化了一个
Man 的对象 , 并声明了一个 Human 的引 ,让它去指向 Man 这个对象意思是说 , Man 这个对象当
Human 看了 .比如去动物园 ,你看见了一个动物 , 不知道它是 什么, "这是什么动物 ? " "这是大熊猫 ! " 2 句话,就是最好的 证明 ,因为不知道它是大熊猫 ,但知道它的父类是动物 ,所以 , 这个大熊猫对象 ,你把它当成其父类 动物看 ,这样子合情合 .这种方式下要注意
new Man(; 的确实例化了 Man 对象 , 所以 ahuman.run( 这个方法
出的 "男人在跑
" 如果在子类 Man 下你 写了一些它独有的方法 比如 eat(,
Human
没有这个方法 , 在调用 eat 方法时 , 一定要注意 制类型转换
((Manahuman.eat(, 这样才可以 ... 对接口来说 情况是类似的
实例:package domatic;//定义超类
superAclass superA {int i = 100;void fun(int j {j = i;System.out.println("This is superA";}}// 定义 superA 的子 subBclass subB extends superA {int m = 1;void fun(int aa {System.out.println("This is subB";}}// 定义 superA

subCclass subC extends superA {int n = 1;void fun(int cc {System.out.println("This is subC";}}class Test {public static void main(String[] args {superA a = new superA(;subB b = new subB(;subC c = new subC(;a = b;a.fun(100;a = c;a.fun(200;}}/** 上述代码中 subB
subC 是超类 superA 的子类 ,我们在类 Test 中声明了 3 个引 用变量
a, b,* c, 通过将子类对象引用赋值给超类对象引用变
量来实现动态方法调用。也许有人会问: * " 为什么 (1(2 不输出: This is superA" * java 的这种机制遵循一个原则: 当超类对象引用变量引用子类对象时 ,* 被引用对象的类型 而不是引用变量的类型决定了调用谁的成员方法 ,* 但是这 个被调用的方法必须是在超类中定义过的 ,* 也就是说被子 类覆盖的方法。 * 所以,不要被上例中
(1(2所迷惑 ,虽然写 a.fun(, 但是由于 (1中的 a b ,* 指向了子类 subB 的一个实例 ,因而(1所调用的 fun( 实际上是子类 subB 的成 员方法 fun(,* 它覆盖了超类 superA 的成员方法

fun( ;同样 (2调用的是子类 subC 的成员方法 fun( * 另外,果子类 继承的超类是一个抽象类 ,虽然抽象类不能通过 new 操作符 例化 ,* 但是可以创建抽象类的对象引用指向子类对象 , 实现运行时多态性。具体的实现方法同上例。 * 不过 ,抽象类 的子类必须覆盖实现超类中的所有的抽象方法 ,* 否则子类 必须被 abstract 修饰符修饰 ,当然也就不能被实例化了 */以上 大多数是以子类覆盖父类的方法实现多 .下面是另一种实 现多态的方法 重写父类方法 1.JAVA 里没有多继承,
一个类之能有一个父类。而继承的表现就是多态。一个父类 可以有多个子类,而在子类里可以重写父类的方法(例如方 print( ,这样每个子类里重写的代码不一样, 自然表现形 式就不一样。这样用父类的变量去引用不同的子类,在调用 这个相同的方法 print( 的时候得到的结果和表现形式就不一 样了,这就是多态,相同的消息(也就是调用相同的方法 会有不同的结果。举例说明: //父类
public class Father{// 类有一个打孩子方法
public void hitChild({}}// 子类 1public class Son1 extends Father{// 重写父类打孩子方法
public void hitChild({System.out.println(" 为什么打我?我做错什 么了! ";}}// 子类
2public class Son2 extends Father{// 重写 父类打孩子方法
public void hitChild({System.out.println(" 我知道错了, 别打了! ";}}// 子类 3public class Son3 extends Father{// 重写父类打孩子方法 public void hitChild({System.out.println(" 我跑,
你打不着! ";}}// 测试类
public class Test{public static void main(String args[]{Father father;father = new Son1(;father.hitChild(;father = new Son2(;father.hitChild(;father = new Son3(;father.hitChild(;}} 都调用了相同的方法,出现了不同
结果!这就是多态的表现!

本文来源:https://www.2haoxitong.net/k/doc/baa44a48ee3a87c24028915f804d2b160a4e8627.html

《java多态性详解父类引用子类对象-.doc》
将本文的Word文档下载到电脑,方便收藏和打印
推荐度:
点击下载文档

文档为doc格式