注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

一切安好

想的有多远,飞的就有多高

 
 
 

日志

 
 

【转载】深层理解父类引用指向子类对象  

2014-11-04 16:16:15|  分类: 技术篇 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

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

假设现在有一个父类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内存.

示例:

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

 

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

 



  评论这张
 
阅读(44)| 评论(26)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017