如何理解父类引用指向子类对象





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

假设现在有一个父类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; 相当于Father f1 = new Son(); 为向上类型转换,可省略,因为子类就是父类,如猫就是动物

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

Son s2 = (Son)f;  //Error 不可直接向下类型转换

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

Son s3 = (Son)f1; 强制类型转换,为向下类型转换,前提是父类引用要先指向子类对象

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

示例:


1 .如果你想实现多态,那么必须有三个条件,父类引用,子类对象,方法覆盖你这里如果Fathor类有一个show()方法,那么形成方法覆盖,那么此时就可以这么写:obj.show(),此刻形成了多态. 

2. 没有方法覆盖,那你这里只能解释为父类引用去访问一个子类的方法,当然,父类引用没有这么大范围的权限,当然会报错 PS:多态实际上是一种机制,在编译时刻,会生成一张虚拟表,来记录所有覆盖的方法,没有被覆盖的方法是不会记录到这张表的.若一个父类引用调用了没有覆盖的子类方法,那么是不符合该表的,那么编译时刻就会报错. 在执行程序的时候,虚拟机会去这张虚拟表中找覆盖的方法,比如引用中实际上存的是一个子类对象引用,那么就会去找子类中的相应的覆盖的方法来执行


    
    
  1. class Father{
  2. void print(){};
  3. }

    
    
  1. class Son extends Father{
  2. void print(){System.out.println( "子类中!");}
  3. void show(){System.out.println( "show 中!");}
  4. }

    
    
  1. class Demo{
  2. public static void main(String args[]){
  3. Father obj= new Son();
  4. obj.print();
  5. obj.show(); //这个调用会报错!,即指向子类对象的父类引用不能调用父类有而子类没有的方法
  6. }
  7. }

定义一个父类类型的引用指向一个子类的对象既可以使用子类强大的功能,又可以抽取父类的共性。
所以,父类类型的引用可以调用父类中定义的所有属性和方法,而对于子类中定义而父类中没有的方法,它是无可奈何的;
同时,父类中的一个方法只有在在父类中定义而在子类中没有重写的情况下,才可以被父类类型的引用调用;
看下面这段程序:

    上面的程序是个很典型的多态的例子。子类Child继承了父类Father,并重载了父类的func1()方法,重写了父类的func2()方法。重载后的func1(int i)和func1()不再是同一个方法,由于父类中没有func1(int i),那么,父类类型的引用child就不能调用func1(int i)方法。而子类重写了func2()方法,那么父类类型的引用child在调用该方法时将会调用子类中重写的func2()。
那么该程序将会打印出什么样的结果呢?
很显然,应该是“CCC”。


    
    
  1. class Father{
  2. public void func1(){
  3. func2();
  4. }
  5. //这是父类中的func2()方法,因为下面的子类中重写了该方法
  6. //所以在父类类型的引用中调用时,这个方法将不再有效
  7. //取而代之的是将调用子类中重写的func2()方法
  8. public void func2(){
  9. System.out.println( "AAA");
  10. }
  11. }
  12. class Child extends Father{
  13. //func1(int i)是对func1()方法的一个重载
  14. //由于在父类中没有定义这个方法,所以它不能被父类类型的引用调用
  15. //所以在下面的main方法中child.func1(68)是不对的
  16. public void func1(int i){
  17. System.out.println( "BBB");
  18. }
  19. //func2()重写了父类Father中的func2()方法
  20. //如果父类类型的引用中调用了func2()方法,那么必然是子类中重写的这个方法
  21. public void func2(){
  22. System.out.println( "CCC");
  23. }
  24. }
  25. public class PolymorphismTest {
  26. public static void main(String[] args) {
  27. Father child = new Child();
  28. child.func1(); //打印结果将会是什么?
  29. }
  30. }

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


    
    
  1. public class A { int a = 1; }
  2. public class B extends A { int a = 2; }
  3. 测试类里调用了这个方法 void compare(){
  4. if( super.a == this.a)
  5. System.out.println( "not overrided");
  6. else
  7. System.out.println( "overrided");}
  8. 控制台出来的是overrided

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



  • 12
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值