ply

吞噬黑暗
posts - 1, comments - 11, trackbacks - 0, articles - 13
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

理解多态AND理解父类引用指向子类对象

Posted on 2011-12-01 22:24 ply 阅读(2832) 评论(0)  编辑  收藏

从对象的内存角度来理解试试.

假设现在有一个父类Father,它里面的变量需要占用1M内存.有一个它的子类Son,它里面的变量需要占用0.5M内存.

现在通过代码来看看内存的分配情况:

Father f = new Father();//系统将分配1M内存.

Son s = new Son();//系统将分配1.5M内存.


因为子类中有一个隐藏的引用super会指向父类实例,所以在实例化子类之前会先实例化一个父类,也就是说会先执行父类的构造函数.

由于s中包含了父类的实例,所以s可以调用父类的方法.


Son s1 = s;//s1指向那1.5M的内存.(可以理解为就是:子类引用指向子类对象)


Father f1 = (Father)s;//这时f1会指向那1.5M内存中的1M内存,即是说,f1只是指向了s中实例的父类实例对象,所以f1只能调用父类的方法(存储在1M内存中),而不能调用子类的方法(存储在0.5M内存中).


Son s2 = (Son)f;//这句代码运行时会报ClassCastException.因为f中只有1M内存,而子类的引用都必须要有1.5M的内存,所以无法转换.


Son s3 = (Son)f1;//这句可以通过运行,这时s3指向那1.5M的内存.由于f1是由s转换过来的,所以它是有1.5M的内存的,只是它指向的只有1M内存.

 


示例:

class Father{

void print(){};

}


class Son extends Father{

void print(){System.out.println("子类中!");}

void show(){System.out.println("show 中!");}

}


class Demo{

public static void main(String args[]){

Father obj=new Son();

obj.print();

obj.show();  //这个调用会报错!

}

}


1 .如果你想实现多态,那么必须有三个条件,父类引用,子类对象,方法覆盖

你这里如果Fathor类有一个show()方法,那么形成方法覆盖,那么此时就可以这么写:obj.show(),此刻形成了多态.

2. 没有方法覆盖,那你这里只能解释为父类引用去访问一个子类的方法,当然,父类引用没有这么大范围的权限,当然会报错

PS:多态实际上是一种机制,在编译时刻,会生成一张虚拟表,来记录所有覆盖的方法,没有被覆盖的方法是不会记录到这张表的.

若一个父类引用调用了没有覆盖的子类方法,那么是不符合该表的,那么编译时刻就会报错.

在执行程序的时候,虚拟机会去这张虚拟表中找覆盖的方法,比如引用中实际上存的是一个子类对象引用,那么就会去找子类中的相应的覆盖的方法来执行

定义一个父类类型的引用指向一个子类的对象既可以使用子类强大的功能,又可以抽取父类的共性。

所以,父类类型的引用可以调用父类中定义的所有属性和方法,而对于子类中定义而父类中没有的方法,它是无可奈何的;

同时,父类中的一个方法只有在在父类中定义而在子类中没有重写的情况下,才可以被父类类型的引用调用;

对多态的理解:多态体现在继承中,所以需要有继承关系,然后子类要重写父类方法,最后父类指向子类(父类本身具有一些方法,这些方法被子类重写了,但调用这些方法时,会自动调子类重写的那些)。

多态具体表现在重写和重载,多态就是类的多种表现方式,比如同名不同参,子类重写父类。

看下面这段程序:

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”。







变量是不存在重写覆盖的!

public class A { int a = 1; }

public class B extends A { int a = 2; }


测试类里调用了这个方法void compare(){

if(super.a == this.a)

System.out.println("not overrided");

else

System.out.println("overrided");}

控制台出来的是overrided

 类中的属性是没有多态性的,即你在引用上面使用属性时,系统只会去找引用的静态类型中的那个属性,而与它的实际类型无关。

静态方法也是没有多态性的。


只有注册用户登录后才能发表评论。


网站导航: