A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 史卜坤 中级黑马   /  2012-7-11 12:16  /  1318 人查看  /  5 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

  • public class Test1 {
  • public int a ;
  • public Test1(){
  • a=3;
  • }
  • public void addFive(){
  • a+=5;
  • }
  • public static void main(String[] args) {
  • Test1 t = new Test2();
  • t.addFive();
  • System.out.println(t.a);
  • }
  • }
  • class Test2 extends Test1{
  • public int a;
  • public Test2(){
  • a=8;
  • }
  • public void addFive(){
  • a+=5;
  • }
  • }


为什么结果是3呢?这个对象的堆空间是怎么搞的?

评分

参与人数 1技术分 +1 收起 理由
刘笑 + 1 赞一个!

查看全部评分

5 个回复

倒序浏览
Test1 t = new Test2();
这句代码的意思就是创建了一个Test1的对象,只不过这个对象在栈内存中却指向了Test2的引用。由于是对Test1进行的对象创建,那么Test1对象创建的同时,Test1中的成员变量,被加载到堆内存中,并进行初始化及赋值操作。但是,他不会去调用方法,因为方法是随着new关键字创建对象时加载到栈内存中的,这个程序中,创建的是Test1对象,加载的却是Test2方法,并不是Test1的方法,所以它不会调用。因为在这两个类中,方法是同名的,Test1,Test2中都有这个方法,所以编译时,不会报错。
回复 使用道具 举报
首先 你创建对象的时候用的是父类引用指向子类对象
那么你在打印t.a的时候,变量a调用的是父类的成员变量a,
在继承的时候,成员变量跟方法是不同的,
多态中,父类引用调用方法时,先找子类,子类有就用子类的
而调用成员变量时,先找父类的,父类有就用父类的
那么你的t.a调用的就是父类的变量a,而你覆盖了父类的addFive()方法,
那么在t.addFive()的时候,调用的确是子类的方法.所以它不会给父类的变量做+=运算..
而打印结果是3而不是默认的0,是因为虽然子类不能继承父类的构造函数,但是子类在new对象的时候,
却必须先执行一个父类的构造函数,这点是程序员没办法避免的,所以也就把3赋给了a.

评分

参与人数 1技术分 +1 收起 理由
刘笑 + 1 赞一个!

查看全部评分

回复 使用道具 举报
孙新强 发表于 2012-7-11 12:58
Test1 t = new Test2();
这句代码的意思就是创建了一个Test1的对象,只不过这个对象在栈内存中却指向了Test ...

这个程序我也运行了        也可以运行呀   只不过结果是    3····
回复 使用道具 举报
本帖最后由 陆强强 于 2012-7-11 13:39 编辑

给你一个图

未命名.JPG (46.38 KB, 下载次数: 18)

未命名.JPG
回复 使用道具 举报
本帖最后由 高原 于 2012-7-11 16:19 编辑

我把你的代码修改了一下,加了些 this 和 打印语句,但不影响程序的运行结果
public class Test1 {
    public int a;

    public Test1() {
        this.a = 3;
        System.out.println("Test1 " + a);  //加上打印语句,方便观察结果
    }

    public int  addFive() {
        return this.a += 5;
    }

    public static void main(String[] args) {
        Test1 t = new Test2();
        System.out.println(t.getClass());       //打印t的类型,这里打印的是运行时类型
        System.out.println(t.addFive());
        System.out.println(t.a);
    }
}

class Test2 extends Test1 {
    public int a;

    public Test2() {
        System.out.println("Test2 " + a);    //先打印一次
        this.a = 8;                      //注意这里将 a 赋值 为 8,
        System.out.println("Test2 " + a);    //再打印一次是为了方便我们分析程序
    }

    public int addFive() {
        return this.a += 5;
    }
}
打印结果为:
Test1 3
Test2 0       //这个打印结果表明:父类的构造方法只是给父类中的 成员变量 i 赋值,和子类中的同名成员变量没关系
Test2 8       //这个时候是子类的构造方法给自己的同名成员变量赋值
class Test2  //运行时类型是子类,
13                //在调用方法时,看的是运行时类型,调用的是子类的方法
3               //在调用同名的成员变量时,看的是编译时类型,调用的是父类的成员变量
看了打印结果,你应该明白了函数调用的顺序了吧,至于父子类构造方法的调用顺序,以及编译和运行时,对于成员变量和成员函数,看左边还是看右边,其他的帖子已经说得很清楚了,你可以自己找找
关键的就是父类子类有同名的成员变量时,构造方法怎样给他们赋值
如果父子类有同名的成员变量,那么,在子类中,就存在两个同名的成员变量了,二者相互不影响。当调用父类的构造方法时,只会给父类中的成员变量赋值,而在子类的方法里,只会给子类的成员变量赋值,二者虽然都是用this进行赋值,但是所指向的成员变量却是不同的!!!另外,在多态调用时,对于这些同名的成员变量,使用父类的引用,调用的就是父类的成员变量,使用子类的引用,调用的就是子类的成员变量

点评

兄弟 ,技术很扎实啊 呵呵  发表于 2012-7-11 14:21
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马