黑马程序员技术交流社区

标题: 用内存分析多态 [打印本页]

作者: 智远    时间: 2015-4-16 18:18
标题: 用内存分析多态
  1. class B extends C{
  2.          public void say(){
  3.                         System.out.println("B");
  4.                 }
  5. }
  6. class C{
  7.                  public void say(){
  8.                          System.out.println("C");
  9.                  }
  10.         }
复制代码
  1. C c =new B();
  2. c.say();
复制代码
从内存的角度帮我分析下,为什么输出B,最好有个图。还有父类初始化后它的属性和方法放在哪里?




作者: 澂澂    时间: 2015-4-16 18:18
C c = new B();
1 new B()  
在堆内容开辟了一块内存存放 这个对象(对象B 0x0001)
2 c = new B()  
表示c指向了对象B(0x0001)
3 c.say ()  
表示调用c指向的对象(这时候是指向对象B,0x0001)的方法say(),   所以输出的是B。
作者: 丢弃不了的力    时间: 2015-4-17 17:29
楼上回答得很好。
作者: jiao142857    时间: 2015-4-17 22:14

欢迎讨论

作者: Chans_宇    时间: 2015-4-17 23:36
我也是初学者,我说说我理解吧。其实Java的跨平台性对原操作系统的资源引有很强的依赖性,毕竟java是后来才出现的面向对象的编程语言,而计算机和微软操作系统的发展是经过漫长的累积的,所以对数据的引用就变得尤为重要,可以节省许多资源。
C c =new B();
这句语句分开在内存中跑的流程是:
C:在内存中加载父类;
c:在栈内存生成变量名为c的局部变量;
Cc:一起是声明引用数据类型是C类(你可以理解为对象c的成员变量或者说是成员属性的引用指向super),c被赋上堆内存的内存地址。
=:赋值。
new:在堆内存中开辟一个空间。
B():我理解为以子类B为规格开辟堆内存,B的构造方法默认带有super();语句,所以在加载B类时其实也同时加载C类,但c作为子类对象的成变量员只能指向一个类成员变量(成员属性),并且在堆内存中只是开辟了一个空间地址赋值给局部变量c(变量c标记为B类的实例化对象,C类的多态引用),可以理解为在堆内存地址相同的空间里划分为两个空间一个用以初始化父类的成员变量并标记为super引用,另一个用于初始化子类的成员变量并标记为this引用,这时我们看左边的引用数据类型是什么类型的,如果实父类C就指向super引用的成员变量,如果是子类B就指向this引用的成员变量。
c.say();这个理解为:其实在加载C类和B类时,它们的非静态方法会被加载在方法区(非静态方法区)里,上面说了是以子类B为规格建立的对象,所以当父类方法被子类覆写时,默认指向了子类的方法(可以理解加进了if的逻辑判断语句),同理,当子类没有覆盖父类的方法时,默认指向父类的没被覆写的方法体,至于子类自己特有的方法(没有覆写父类方法的特有方法)正常引用即可。



作者: 342508558    时间: 2015-6-24 15:43
JVM就是这么设计的。JVM会持有一个类的所有方法的列表,对Class C 他实际有
B. say()和C.say()两个方法。在执行过程中,JVM会根据对象的实际类型选择适当的方法去调用。调用哪个方法是由JVM决定的,和内存没有关系。
对象是C的实例就调用C.say(),是B的实例就调用B.say();如果C没有重写say()方法,那么B.say()就是适当的方法。因为是在执行的时候才决定到底调用哪个方法的所以叫做动态绑定。
假如C和B都有一个int i 的变量的话,编译的时候,访问的变量的内存位置就确定了,对象使用B声明就会访问到B的变量,所以变量是静态绑定。




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2