黑马程序员技术交流社区

标题: 请帮忙解答疑惑 [打印本页]

作者: 完美恋爱    时间: 2013-12-5 18:22
标题: 请帮忙解答疑惑
本帖最后由 完美恋爱 于 2013-12-6 06:20 编辑

class Super
{
        int i=0;

        public Super()
        {
                i+=2;
        }
}
class Demo extends Super
{
        public Demo()
        {
                i+=5;                                
        }
        public static void main(String[] args)
        {
                int i=4;
                Super d=new Demo();
                System.out.println(d.i);
        }
}
为什么输出的结果是7呢?调用的成员变量i 应该是父类的啊!那应该是2才对啊!


作者: kongling    时间: 2013-12-5 18:57
Super d=new Demo();Demo为Super的子类,创建对象时,先调用父类的构造方法i+=2;,再调用子类的构造方法i+=5;  最终结果是7
作者: 完美恋爱    时间: 2013-12-5 19:13
sorry,你好像解答错了
作者: ily521125    时间: 2013-12-5 20:32
本帖最后由 ily521125 于 2013-12-5 20:54 编辑

继承中的构造器调用:
因此在创建一个Demo()对象时,无论Demo类中有无构造方法,都会去调用Super中的构造方法,并继承了Super的成员属性i
如果Demo类中无构造方法即没有语句 public Demo(){ i+=5}; 则在创建Demo()对象时,默认调用Super类的构造方法,得到 i 的结果为2;
如果Demo类中有构造方法即有语句 public Demo(){ i+=5 }; 则在创建Demo()对象时,也会调用Super类的构造方法,此时代码相当
  1. public Demo()
  2. {
  3.               // Super(); 该语句是默认产生的去调用父类构造方法
  4.                i+=5;
  5. }
复制代码

在调用父类构造方法后,i=2,就继续执行 i+=5 所以在完成Demo()对象创建后成员变量 i 结果是7



作者: 完美恋爱    时间: 2013-12-5 20:56
ily521125 发表于 2013-12-5 20:32
继承中的构造器调用:

可是在子类中的i和父类中的i不是一个i吧!而且不是只能调用父类本身的成员变量吗?那为什么还会把子类中的算进去呢!请再详细说明一下
作者: ily521125    时间: 2013-12-5 21:05
本帖最后由 ily521125 于 2013-12-5 21:13 编辑
完美恋爱 发表于 2013-12-5 20:56
可是在子类中的i和父类中的i不是一个i吧!而且不是只能调用父类本身的成员变量吗?那为什么还会把子类中 ...


具体的说,就是若果你没有继承Super类的话,你Demo类中的构造方法中写的i+=5是不对的会报错,因为Demo类中并没有成员变量i,你主方法中定义的int i=4,是作用在主方法中的,和成员变量没有任何关系。Demo类继承了Super类会也会继承Super类的成员变量i,因此i+=5中的i就是继承来的

也就是说,你是先继承类Super类中的成员变量 i ,才可以在Demo类构造方法中写i+=5这这句话,不然你都没有定义i,何来的i+=5呢

作者: 刘敏    时间: 2013-12-5 21:32
Super d=new Demo();这句话内涵很丰富:
1. 先在堆内存中分配一个Demo对象,初始化 i=0。
2. 执行父类的构造函数   i+=2;//此时i=2
3. 执行自己的构造函数  i+=5;// 此时i=7。

注意:Demo自己在内存中并没有自己的i,这个变量i是Super的,Demo继承Super就自动拥有了这个i。

作者: 完美恋爱    时间: 2013-12-5 21:33
ily521125 发表于 2013-12-5 21:05
具体的说,就是若果你没有继承Super类的话,你Demo类中的构造方法中写的i+=5是不对的会报错,因为Demo类 ...

原来如此,我懂啦!谢谢了哈!
作者: 完美恋爱    时间: 2013-12-5 21:38
ily521125 发表于 2013-12-5 21:05
具体的说,就是若果你没有继承Super类的话,你Demo类中的构造方法中写的i+=5是不对的会报错,因为Demo类 ...

还有一个疑惑,确实,i是继承过来的,可是d是父类的引用,所以调用的d.i的i应该是在父类范围内的i,就是说i的值应该是在父类范围内的,i+=5是在 子类范围内的,照常的话不应该算上去才对啊!
作者: 完美恋爱    时间: 2013-12-5 21:38
刘敏 发表于 2013-12-5 21:32
Super d=new Demo();这句话内涵很丰富:
1. 先在堆内存中分配一个Demo对象,初始化 i=0。
2. 执行父类的构 ...

还有一个疑惑,确实,i是继承过来的,可是d是父类的引用,所以调用的d.i的i应该是在父类范围内的i,就是说i的值应该是在父类范围内的,i+=5是在 子类范围内的,照常的话不应该算上去才对啊!
作者: ily521125    时间: 2013-12-5 21:47
完美恋爱 发表于 2013-12-5 21:38
还有一个疑惑,确实,i是继承过来的,可是d是父类的引用,所以调用的d.i的i应该是在父类范围内的i,就是 ...

java允许父类的引用变量引用它的子类的实例
Super d=new Demo();这种类型转换时自动完成的,父类转换成了子类,所以d.i就是demo类中的
作者: 刘敏    时间: 2013-12-5 21:51
完美恋爱 发表于 2013-12-5 21:38
还有一个疑惑,确实,i是继承过来的,可是d是父类的引用,所以调用的d.i的i应该是在父类范围内的i,就是 ...

你的父类的i是没有设权限的,如果设置为private,子类是没办法修改的;但是你的是默认,父类是可以修改的。你想要的那个结果应该这样写:class Super
{
        int i=0;

        public Super()
        {
                i+=2;//这个修改的是子类的i
        }
}
class Demo extends Super
{
        int i;//这个是子类自己的


        public Demo()
        {
                i+=5;  //这个修改的是父类的i,这个i=5; 如果没有上面的定义,这里就是修改父类的        
        }
        public static void main(String[] args)
        {
                int i=4;
                Super d=new Demo();
                System.out.println(d.i); //d.i 子类和父类都有i,编译器会让d调用父类的i;。
        }
}


作者: 四五六七八    时间: 2013-12-5 22:01
完美恋爱 发表于 2013-12-5 21:38
还有一个疑惑,确实,i是继承过来的,可是d是父类的引用,所以调用的d.i的i应该是在父类范围内的i,就是 ...

哥们  有一点关键的你没有搞明白   Super d=new Demo();这句话是在堆内创建一个Demo对象  并没有Super对象 d引用的是Demo对象     理解了这点就不疑惑了
作者: 完美恋爱    时间: 2013-12-5 22:02
ily521125 发表于 2013-12-5 21:47
java允许父类的引用变量引用它的子类的实例
Super d=new Demo();这种类型转换时自动完成的,父类转换成了 ...

class Super
{
        int i=0;
       
        public Super()
        {
                i+=2;
        }
}
class Demo extends Super
{
        public Demo()
        {
                i+=5;
        }
        public static void main(String[] args)
        {
                int i=4;
                Super d=new Demo();
                Demo a = (Demo)d;
                i++;
                System.out.println(a.i);
        }
}
这个结果为什么还是7啊?
作者: 刘敏    时间: 2013-12-5 22:08
本帖最后由 刘敏 于 2013-12-5 22:10 编辑

其实,你要知道,你的代码中,不管是父类对象.i, 还是子类对象.i,实际在内存中只有一个i,修改i就会改变它的值。需要补下对象的内存分布啊!                int i=4; //这个是main()函数的局部变量,不是Demo的成员变量!!
                Super d=new Demo();
                Demo a = (Demo)d;
                i++;
                System.out.println(a.i);


作者: 完美恋爱    时间: 2013-12-5 22:10
四五六七八 发表于 2013-12-5 22:01
哥们  有一点关键的你没有搞明白   Super d=new Demo();这句话是在堆内创建一个Demo对象  并没有Super对 ...

你说的我知道,父类引用指向子类对象,这跟我问的 那个没关系
作者: ily521125    时间: 2013-12-5 22:12
完美恋爱 发表于 2013-12-5 22:02
class Super
{
        int i=0;


那你认为是几呢?你把父类引用子类的实例有转换成子类,它还是具有父类和子类的属性啊,这块儿我建议你看看关于多态的内容
作者: 完美恋爱    时间: 2013-12-5 22:17
ily521125 发表于 2013-12-5 22:12
那你认为是几呢?你把父类引用子类的实例有转换成子类,它还是具有父类和子类的属性啊,这块儿我建议你看 ...

我感觉应该是5啊!因为向下转型后调用的就是自身的i了啊!
作者: ily521125    时间: 2013-12-5 22:26
完美恋爱 发表于 2013-12-5 22:17
我感觉应该是5啊!因为向下转型后调用的就是自身的i了啊!

如果你非要让它输出是主函数里的i的话,就直接输出i
  1. class Super
  2. {
  3.         int i=0;
  4.         
  5.         public Super()
  6.         {
  7.                 i+=2;
  8.         }
  9. }
  10. class Demo extends Super
  11. {
  12.         public Demo()
  13.         {
  14.                 i+=5;
  15.         }
  16.         public static void main(String[] args)
  17.         {
  18.                 int i=4;
  19.                 Super d=new Demo();
  20.                 Demo a = (Demo)d;
  21.                 i++;
  22.                 System.out.println(i);
  23.         }
  24. }
复制代码
这样结果就是5了
作者: 四五六七八    时间: 2013-12-5 22:26
完美恋爱 发表于 2013-12-5 22:10
你说的我知道,父类引用指向子类对象,这跟我问的 那个没关系

那个i不是父类范围  是子类继承父类之后属于子类自己的  你可以理解为父类也有一个i 只是在内存中只有一个Demo对象(只有子类范围的i)
如果还不理解     那可能就是你认为   调用父类构造方法会创建父类对象  实际上调用构造方法并不一定会创建对象                 
作者: 完美恋爱    时间: 2013-12-5 22:37
ily521125 发表于 2013-12-5 22:26
如果你非要让它输出是主函数里的i的话,就直接输出i这样结果就是5了

这个我之前试过了,就是我想说的意思是,我明明都向下转型了,这时调用的i难道不应该是子类里的i吗?
举个例子:
class Fu
{
     int  i  =  3;
}
class Zi   extends  Fu
{
     public  static  void  main(String[]  args)
     {
           int  i  =  5;
           Fu  f  =  new  Zi();
           System.out.println(f.i);  //  i  ==  3;
           Zi  z  =  (Zi)f;
           System.out.println(z.i);  //  i  ==  3;
     }
}
-------------------------------------------------------
class Fu
{
     int  i  =  3;
}
class Zi   extends  Fu
{
     int  i  =  5;
     public  static  void  main(String[]  args)
     {
           Fu  f  =  new  Zi();
           System.out.println(f.i);  //  i  ==  3;
           Zi  z  =  (Zi)f;
           System.out.println(z.i);  //  i  ==  5;
     }
}
为什么这两个最后的i的值会不同呢?
作者: 简★零度    时间: 2013-12-5 23:12
下次问题解决了就把类型改成提问结束!谢谢!
作者: ily521125    时间: 2013-12-5 23:20
完美恋爱 发表于 2013-12-5 22:37
这个我之前试过了,就是我想说的意思是,我明明都向下转型了,这时调用的i难道不应该是子类里的i吗?
举 ...


上面的程序子类中没有定义成员变量 i ,不用说输出的两个 i 值都是继承父类的 i;
下面的程序,在执行Zi z=(Zi)d之前使用的是 是父类的i ,执行之后输出z.i就是子类将父类覆盖的i了




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