黑马程序员技术交流社区

标题: 对象变量的访问,我见鬼了,求牛人解释!!!! [打印本页]

作者: 周毅中    时间: 2012-4-25 09:31
标题: 对象变量的访问,我见鬼了,求牛人解释!!!!
先上代码,大家看看到底i是多少?
  1. public class MyTest {
  2.         public static void main(String[] args) {
  3.                 new Dog();
  4.         }
  5. }

  6. class Animal {
  7.         private int i = 2;
  8.        
  9.         public Animal() {
  10.                 this.display();
  11.         }
  12.        
  13.         public void display() {
  14.                 System.out.println(i);
  15.         }
  16. }

  17. class Dog extends Animal {
  18.         private int i = 22;
  19.        
  20.         public Dog() {
  21.                 i = 222;
  22.         }
  23.        
  24.         public void display() {
  25.                 System.out.println(i);
  26.         }
  27. }
复制代码
求牛人解释。。。怎么也猜不到打印的竟然是0。

作者: 王勃    时间: 2012-4-25 09:39
哥表示,能解答,在调试中。。。。。稍等哈
作者: 刘永菲    时间: 2012-4-25 09:40
调试了一遍,关系明确,看不出什么问题,同惑,共求解。
作者: 王勃    时间: 2012-4-25 09:42
本帖最后由 王明(1988) 于 2012-4-25 09:45 编辑

看来见鬼的不是你啊,是你被鬼玩了。哈哈哈哈{:soso_e144:}{:soso_e144:}
我给程序加了注释,你仔细看下啊,给你上我改过的(加注释了)代码:
  1. package com.heima;

  2. public class MyTest {
  3. public static void main(String[] args) {
  4. new Dog();
  5. }
  6. }

  7. class Animal {
  8. private int i = 2;

  9. public Animal() {
  10. //由于你创建的是Dog对象,要明白this在这里究竟指向谁?我调用getClass()来看看
  11. System.out.println(this.getClass());
  12. this.display();
  13. }

  14. public void display() {
  15. System.out.println("Animal的display方法");
  16. System.out.println(i);
  17. }
  18. }

  19. class Dog extends Animal {
  20. private int i = 22;

  21. public Dog() {
  22. super();//加上super()是为了更直观点
  23. i = 222;
  24. }

  25. public void display() {
  26. System.out.println("Dog的display方法");
  27. System.out.println(this.i);
  28. }
  29. }
复制代码
现在给你解释。
首先new Dog();这时jvm给你在堆内存中申请空间,此刻i=0,而在调用父类构造方法之前,先去初始化实例变量的值和初始化块(先静态,后非静态),这时在进入构造函数之前i被初始化为2(这个i作为一份拷贝存在Dog当前对象中)。进入父类构造方法后,由于你使用this.display();你用到了this,这很关键,要明白this究竟指向谁呢?很显然this指向的不是Animal而是Dog(虽然这个this你写在Animal中),所以接下来调用的是Dog的display()方法而不是Animal的display()方法,在Dog的display方法中你打印i的值(注意这是的打印的i不是之前的拷贝,是原始的i),由于此i是在Dog类中,并且在Dog中没有进行初始化,所以仍是0,当this.dispaly();完了之后要先初始化Dog的实例变量值,这是i(不是拷贝i,拷贝i为2)为22,执行Dog构造方法中的i=222;后i(不是拷贝i)为222。注意哦亲,你的打印语句是在Dog的display方法里面,i是那时的i非此时的i。

其实你可以给程序加断点,进行调试跟踪,对i的变化那是一目了然。等会给你上图。。。

作者: 王勃    时间: 2012-4-25 09:43
代码忘上了等我修改
作者: 王勃    时间: 2012-4-25 09:51
表示你这个问题,很经典,很能说明对象在创建时,变量时怎么变化的。。。。
现在给你上我调试的图片,有图有真相。。。。。。我在其中加了断点,你注意哦,绿色的断点。
1.

作者: 王勃    时间: 2012-4-25 10:04
2.我step into(F5)进入父类,在调用父类构造方法前,先实例化拷贝i,注意变化哦,

F6后,上图:



作者: 王勃    时间: 2012-4-25 10:25
好,图片能看清楚,我继续。。。。
直接上图了,不文字说明了。


见证奇迹的时刻到了。输入的是0。

下来的i变化,你自己跟踪调试,我就不上图。已经打印你的i了,之后i会变成22,再变成222,此时i非彼时i。
调用毕老师的话,O了。{:soso_e128:}{:soso_e128:}
作者: 周毅中    时间: 2012-4-25 10:38
感谢王同学的解答,对于对象的创建,以及其中变量的变化,我从此不会迷糊,也调用毕老师的一句话:O了。
作者: 沈样    时间: 2012-4-25 10:40
我是这样分析不知道对不对,new Dog();这个会在堆内存中先找到Dog.class并加载进来,但是在加载之前,方法区内的方法就已经加载了,因为public Dog() 的构造方法中在
i = 222;之前是先调用父类的默认构造方法,在父类默认函数调this.display() 时,this是调用他的类,即dog类的display,所以打印的是dog的i,因为方法区内的要先于对象创建,
所以调用时i还没初始化,如果加static关键字加载到方法区内,就能打印出i的值,不过在这里我也不太确定,因为属性初始化要先于构造函数,有知道的兄弟也发个信息给我,
共同学习一下
作者: 李斌    时间: 2012-4-25 10:50
学习了~~

作者: 沈样    时间: 2012-4-25 10:53
在网上找了一下,或许可以解释一下:(1)设置成员的值为默认的初始值(0,false,null)。

  (2)调用对象的构造方法(但是还没有执行构造方法体)。
  (3)调用父类的构造方法。
    (4)使用初始化程序和初始块初始化成员。
  (5)执行构造方法体。
这样就可以解释为什么构造方法前为什么没有初始化成员值

作者: 王勃    时间: 2012-4-25 10:59
本帖最后由 王明(1988) 于 2012-4-25 11:00 编辑

额,沈同学,加载顺序你错了,还有方法区的理解也有问题、、、、
在加载main方法后,静态变量不管父类还是子类的都执行了,然后才是父类和子类的的普通变量和构造器。这是因为,当要创建子类这个对象时,
发现这个类需要一个父类,所以把父类的.class加载进来,然后依次初始化其普通变量和初始化代码块,最后才是构造器,然后可以开始子类的工作,
把子类的.class加载进来,在做子类的工作。而方法区的代码是存放代码的地方,虽然jvm在程序执行前将代码放进去,但代码是静态的,还没真正执行,在执行时,初始化Dog中的i为默认的0.这个样子。

给你一个静态变量,静态初始化块,和普通变量,以及构造器的加载顺序:
父类--静态变量
父类--静态初始化块
子类--静态变量
子类--静态初始化块
*************in main***************
父类--变量
父类--初始化块
父类--构造器
子类--变量
子类--初始化块
子类--构造器
*************second subClass***************
父类--变量
父类--初始化块
父类--构造器
子类--变量
子类--初始化块
子类--构造器
作者: 王勃    时间: 2012-4-25 11:00
我给你代码,你自己看看吧>>>>>
作者: 王勃    时间: 2012-4-25 11:07
代码:
  1. package com.heima;

  2. class Parent {
  3. // 静态变量
  4. public static String p_StaticField = "父类--静态变量";
  5. // 变量(其实这用对象更好能体同这一点,如专门写一个类的实例)

  6. // 如果这个变量放在初始化块的后面,是会报错的,因为你根本没有被初始化
  7. public String p_Field = "父类--变量";
  8. // 静态初始化块
  9. static {
  10. System.out.println(p_StaticField);
  11. System.out.println("父类--静态初始化块");
  12. }
  13. // 初始化块
  14. {
  15. System.out.println(p_Field);
  16. System.out.println("父类--初始化块");
  17. }

  18. // 构造器
  19. public Parent() {
  20. System.out.println("父类--构造器");
  21. }
  22. }

  23. public class SubClass extends Parent {
  24. // 静态变量
  25. public static String s_StaticField = "子类--静态变量";
  26. // 变量
  27. public String s_Field = "子类--变量";
  28. // 静态初始化块
  29. static {
  30. System.out.println(s_StaticField);
  31. System.out.println("子类--静态初始化块");
  32. }
  33. // 初始化块
  34. {
  35. System.out.println(s_Field);
  36. System.out.println("子类--初始化块");
  37. }

  38. // 构造器
  39. public SubClass() {
  40. // super();
  41. System.out.println("子类--构造器");
  42. }

  43. // 程序入口
  44. public static void main(String[] args) {
  45. System.out.println("*************in main***************");
  46. new SubClass();
  47. System.out.println("*************second subClass***************");
  48. new SubClass();
  49. }
  50. }
复制代码
输出结果:


作者: 王勃    时间: 2012-4-25 11:12
总之,原理都是文字,那里有实践来的更直接啊,所以i是多少,自己断点调试跟踪,一目了然,这个问题到此,大家都一起O了吧。。。。
{:soso_e128:}
作者: 高彰谦    时间: 2012-4-25 11:14
王明讲的已经非常清楚了,我就不多赘述了,支持王明的讲解,实在是很给力啊!
作者: 王勃    时间: 2012-4-25 11:26
哥表示是1988年的王明,论坛里还有和我重名的,注册王明不成功,郁闷的很那,只能说老爸太崇拜导致长征的左倾罪人啊。
送上一句:保持写下梦想时的状态,不要因为一开始的失败而放弃,80后三十而立,为明天伏笔,今天只有努力。
一起向上吧,少年!
作者: 陈忠    时间: 2012-4-25 11:52
王明(1988) 发表于 2012-4-25 11:07
代码:输出结果:

理解的那是一个透彻啊 ,佩服!
作者: 罗旭维    时间: 2012-4-25 12:00
我也改了一份代码,这份代码可以找到根本原因:
  1. package test;
  2. public class MyTest {
  3.         public static void main(String[] args) {
  4.                 new Dog();
  5.         }
  6. }

  7. class Animal {
  8.         private int i = 2;
  9.         
  10.         public Animal() {
  11.                         System.out.println(this.getClass().getName());
  12.                 this.display();
  13.         }
  14.         
  15.         private void display() {
  16.                       System.out.println("Animal method");
  17.             System.out.println(i);
  18.         }   
  19. }

  20. class Dog extends Animal {
  21.         private int i = 22;
  22.         
  23.         public Dog() {
  24.                 this.i = 222;
  25.         }
  26.         
  27.         private void display() {
  28.                 System.out.println("Dog method");
  29.                 System.out.println(this.i);
  30.         }
  31. }
复制代码
修改只是将display声明改为private,结果是Animal构造函数里的this指针的确是子类Dog的,但调用的display方法却是Animal!这似乎乱套了?
关键在于java虚函数的机制,子类重新定义了display方法,用多态的机制解释,当创建的是子类对象那这个display调用的就是子类定义的display。
实际可以理解为子类的方法覆盖(替换)了父类的方法,没有产生新的方法。而声明改成private后,私有成员不能外界(包括子类)访问,所以子类虽然重新定义了display方法,但他没有覆盖(替换)掉原来的display方法,而是产生了一个新的方法。
在java里你声明了一个类一个方法那它的所有代码都固定了,代表java类的那一段字节码是不会变的。
Animal构造函数的代码也是固定的。里面调用的display方法单纯的理解也是一个函数地址(标识、函数名),不管写成this.display()还是就只写成display(),它调用的还是那个函数。在声明为public的情况下display被子类重新定义的方法覆盖(替换)了,所以调用的是子类实现的display。而在声明为private的情况下,编译器是以某种规则产生了新的方法,子类的display有新的函数地址(标识、函数名)。父类Animal的构造函数方法代码又没变化,所以它调用的还是父类那个display。
作者: 王勃    时间: 2012-4-25 12:16
罗旭维 发表于 2012-4-25 12:00
我也改了一份代码,这份代码可以找到根本原因:修改只是将display声明改为private,结果是Animal构造函数里 ...
修改只是将display声明改为private,结果是Animal构造函数里的this指针的确是子类Dog的,但调用的display方法却是Animal!这似乎乱套了?

这不可能,我测试了你的代码,明明是Dog的display()方法被调用。。。。
看图:
  1. package com.heima;

  2. public class MyTest2 {
  3.     public static void main(String[] args) {
  4.             new Dog();
  5.     }
  6. }

  7. class Animal2 {
  8.     private int i = 2;
  9.    
  10.     public Animal2() {
  11.          System.out.println(this.getClass().getName());
  12.          this.display();
  13.     }
  14.    
  15.     private void display() {
  16.         System.out.println("Animal method");
  17.         System.out.println(i);
  18.     }   
  19. }

  20. class Dog2 extends Animal2 {
  21.     private int i = 22;
  22.    
  23.     public Dog2() {
  24.             this.i = 222;
  25.     }
  26.    
  27.     private void display() {
  28.             System.out.println("Dog method");
  29.             System.out.println(this.i);
  30.     }
  31. }
复制代码
我把名字改成都加2,因为我的包里之前有个Animal和Dog,冲突了。
输出结果:

作者: 王勃    时间: 2012-4-25 12:21
关键在于java虚函数的机制,子类重新定义了display方法,用多态的机制解释,当创建的是子类对象那这个display调用的就是子类定义的display。
实际可以理解为子类的方法覆盖(替换)了父类的方法,没有产生新的方法。而声明改成private后,私有成员不能外界(包括子类)访问,所以子类虽然重新定义了display方法,但他没有覆盖(替换)掉原来的display方法,而是产生了一个新的方法。

在这个例子里,只创建了子类,并没有调用什么display方法,拿多态解释是错误的,亲,new Dog()这个对象还没调用任何方法,只是在构造器中调用类中的成员方法,你觉得这事情跟多态有关系吗!!!!!
作者: 王勃    时间: 2012-4-25 12:24
罗旭维 发表于 2012-4-25 12:00
我也改了一份代码,这份代码可以找到根本原因:修改只是将display声明改为private,结果是Animal构造函数里 ...

你试试吧,虽说子类访问父类的private 方法不行,我在构造对象时就调用,你觉得我就不能调用了吗?
那你说单例模式有该如何解释。。。。
作者: 罗旭维    时间: 2012-4-25 12:36
王明(1988) 发表于 2012-4-25 12:24
你试试吧,虽说子类访问父类的private 方法不行,我在构造对象时就调用,你觉得我就不能调用了吗?
那你 ...

这位同学,我不觉得我哪里有问题,我这边运行没问题,用的是1.6的jdk。
作者: 王勃    时间: 2012-4-25 12:39
那,我表示我见鬼了。。。。{:soso_e120:}
作者: 罗旭维    时间: 2012-4-25 12:40
王明(1988) 发表于 2012-4-25 12:24
你试试吧,虽说子类访问父类的private 方法不行,我在构造对象时就调用,你觉得我就不能调用了吗?
那你 ...

这些是基础知识。
作者: 王勃    时间: 2012-4-25 12:41
罗旭维 发表于 2012-4-25 12:36
这位同学,我不觉得我哪里有问题,我这边运行没问题,用的是1.6的jdk。

我copy你的代码,只改了个2,就这样,你仔细看看。。。
不是你见鬼了,就是我见鬼了。。。
作者: 王勃    时间: 2012-4-25 12:42
罗旭维 发表于 2012-4-25 12:36
这位同学,我不觉得我哪里有问题,我这边运行没问题,用的是1.6的jdk。

我的意思主要是,你用多态来解释是错误的,这里还未曾体现出多态。。。
仔细想想哦。。。
作者: 罗旭维    时间: 2012-4-25 12:44
王明(1988) 发表于 2012-4-25 12:42
我的意思主要是,你用多态来解释是错误的,这里还未曾体现出多态。。。
仔细想想哦。。。 ...

受够了,好吧,我无语。
作者: 王勃    时间: 2012-4-25 12:49
本帖最后由 王明(1988) 于 2012-4-25 14:30 编辑
罗旭维 发表于 2012-4-25 12:44
受够了,好吧,我无语。

额,我运行n遍,还是上面的结果。。。

作者: 罗旭维    时间: 2012-4-25 13:04
王明(1988) 发表于 2012-4-25 12:24
你试试吧,虽说子类访问父类的private 方法不行,我在构造对象时就调用,你觉得我就不能调用了吗?
那你 ...

我还是提醒下你吧,如果你运行的是你贴出来的代码,那请注意你的代码,你测试新建了Dog2类但在main函数里你new 的是Dog类!
作者: 王勃    时间: 2012-4-25 13:10
罗旭维 发表于 2012-4-25 13:04
我还是提醒下你吧,如果你运行的是你贴出来的代码,那请注意你的代码,你测试新建了Dog2类但在main函数里 ...

额,我错了,多谢提醒。刚才因为Dog2只改了类,没改new Dog2();又调到我包里面的那个Dog类中去了。

嗯,不过我认为这个例子没涉及到多态,因为我没用new Dog2()去调用方法。你认为呢,,,
作者: 王勃    时间: 2012-4-25 13:11
罗旭维 发表于 2012-4-25 13:04
我还是提醒下你吧,如果你运行的是你贴出来的代码,那请注意你的代码,你测试新建了Dog2类但在main函数里 ...

再次表示,罗同学很细心啊,谢谢:'(
作者: 真真姐    时间: 2012-4-25 15:26
本帖最后由 杨国祯 于 2012-4-25 17:35 编辑

这个问题看到,兄弟们很纠结啊!!
不同的看法实际上很正常,真理就是一个讨论的过程才出来的,说实话大家的解释我没怎么看,因为思路不一样!
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
这几天实际上大家搜搜这一类的我有几个回答,加起来会综合的理解,今天的这个不仅仅是执行顺序的问题,还有子类父类的问题,可能部分比较复杂!这里简单的简述下:
  1. 这里简单谈谈我的看法!我们要理解程序中代码的加载顺序 :静态块——>静态变量——>成员变量——>构造方法——>静态方法(执行到才加载)
  2. 一个完整执行过程都是这样的,本例中没有任何的静态变量,静态方法;当前过程中的变量,比如i只有执行到了才会加载,所以就是一个正常的执行过程
复制代码
所以执行顺序是:
  1. A、执行new Dog();
  2. B、执行Dog()无参构造方法,执行里面默认的super(),为什么会这样说,看我的其他回复,解释的很详细!
  3. C、到super()这了,当然要去看看父类有什么指示,跳转至Animal()无参构造函数,由于父类重写了系统默认提供的无参的构造函数,所以要先执行父类构造函数里面的语句!
  4. D、继续执行,这里的this注意是指代的谁,记得这句话谁调用就指代谁,这里可以用 this.getlass()检测 , 看结果,这里肯定是Dog,所以this指代Dog
  5. E、这是一个容易搞糊涂的地方,注意了   this既然是Dog类,这里注意子类的方法是不是跟父类的方法同名,这是什么呢,是不是重写啊, 子类的方法对于父类的方法重写了,你说执行谁的方法!
  6. F、最后执行子类重写的方法,子类的变量加载了没,还没有,但是系统会给赋初值,i是多少当然是0,所以结果就出来了
复制代码
  1. 王明(1988)童鞋,debug模式写的很好,我不知道他的是不是跟我的一样,但是他用的方法很好值得推广
复制代码
扩展:
  1. 这里如果把,两个变量都变成static的猜猜结果会怎么样
  2. private static int a = 2
  3. private static int a = 22
  4. 注意:你会发现结果不是0了, 是22了,为什么现在知道了吧,因为执行顺序,静态变量先加载了,才执行的new Dog()!!
复制代码
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
这里是综合的知识点,对于基本的东西一定深挖,这样才能理解透彻!!
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
最后用代码给你们标注请代码执行过程:
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
public classMyTest
           {
              public static void main(String[] args)
                  {
                     new Dog();                      //第一步
                  }
           }
       class Animal
           {
              private int i = 2;
              public Animal()                       //第四步,跳过来发现父类重写了系统默认的无参构造方法,于是执行里面语句
                  {
                     this.display();                  // 第五步,发现this是代表Dog,知道为什么吗! 因为谁调用就代表谁,这个忘了没,上面刚提到了!不信?自己getClass。
                  }
              public void display()
                  {
                     System.out.println(i);
                  }
           }
       class Dog extends Animal
           {
              private int i = 22;
              public Dog()                           // 第二步
                  {  //super                           // 第三步,这里系统默认提供了一个super(),为什么,记住一句话,子类要去请示父类,是系统默认的   
                     i = 222;   
                  }
              public void display()               // 第六步 为什么是这,毕老师要敲你了啊,方法名一样,参数一样是什么,是不是重写,重写怎么执行!
                  {
                     System.out.println(i);        // 第七步,给 i 赋值的代码块执行到了没,没有吧,i的值是多少,是不是系统默认提供的!
                  }
           }


这样的话是不是清晰了呢,执行过程有时候就是比较复杂,但是我们知道了之后发现过程应该按照语法来,是不是!

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
还有两种增补情况,帖子太长,不让发,只好另外书写,请看下一贴!!
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------




作者: 王勃    时间: 2012-4-25 15:51
本帖最后由 王明(1988) 于 2012-4-25 15:52 编辑
杨国祯 发表于 2012-4-25 15:26
这个问题看到,兄弟们很纠结啊!!
不同的看法实际上很正常,真理就是一个讨论的过程才出来的,说实话大家 ...

我们先简单的程序中代码的加载顺序 :静态块——>静态变量——>成员变量——>构造方法——>静态方法(执行到才加载)

额,后面的对前面的就错了,静态块,静态变量这个顺序是和你在类中写的顺序相关的!不是绝对的静态块在前,静态变量在后,你可以试一下,静态变量写在静态初始化块前面,顺序就反过来了。
嗯,你总结的A,B,C,D,E,F很简洁,但你调试会发现,里面this下面有两个 i ,我的理解是,下面的是拷贝,
上面的是原始 i 。因为new的是Dog类,应该只有一个实例变量,那么多的一个,我的理解是拷贝,因为父类并没有实例化啊,你认为呢?
还有
这里如果吧 两个变量都变成static的猜猜结果会怎么样

既然new了对象,咱们去搞静态的类变量,好像没人这么干吧,不过你说的是对的。实际开发没人这么干滴。。。
作者: 真真姐    时间: 2012-4-25 16:20
本帖最后由 杨国祯 于 2012-4-27 10:43 编辑

一定要深入理解相关知识,知道你混淆概念了,应该很多人有这个疑问,
  1. 比如说静态块,什么是静态块,是静态函数吗!其实不是的
  2. static {
  3. }
  4. 就是一个静态块,里面有相应的语句,他绝对是第一位加载的,不论你放到哪里这个一定要注意!!
复制代码
我有另一个回复,专门介绍执行顺序,有心的童鞋可以去看看,里面总结的很详细!

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
接上一贴:增补两种情况,童鞋们看了会有益处的!!
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
第一种  在问题的例子里面 既然 Dog的 i 变量是0,那么Animal中的变量 i 是多少呢!,这个问题有没有想过
有个童鞋可能会说,Animal中的变量也不是静态的不会提前加载,当然也是o了,这样想的话就出错了,其实Anmal中的i的值是2,
不好理解吧! 接着看,
先抛开继承不说,我们就只有一个方法,我们在自己的方法里面实例化一个对象,执行的时候是怎么执行的清楚吗?
实际上执行的时候,每new一个当前类对象   如 Animal  ani  =  new Animal的时候,都需要吧Animal这个类里面的所有属性执行一遍,然后在执行构造方法,
你又问为什么呢?不这样的话你说Animal类的对象的属性从何而来,不都是系统的默认值了吗
class Animal
           {
              private int i = 2;             // new Animal()的时候我们会发现这个第一个执行,为的就是给Animal加载属性值

              public Animal()                //然后这个是第二步                     
                  {
                     this.display();           // 这个是第三步,这里的 this是谁,很简单的,谁调用就是谁
                  }
              public void display()
                  {
                     System.out.println(i);  //打印出来的i是多少呢? 当然是2了,不然你new出来的对象都没有属性,还如何操作!
                  }
           }
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
第二种
大家注意看这里的代码都是 private的,有的同学会想了,我要是吧private改成public会怎么样,是不是跟private一样啊 ,因为你打印的是Dog,这里面的变量根本还没有初始化
那你又说了; 我给他们加上static,也就是我补充的,会怎么样呢,答案就是 ,Dog中的i变化了,就是22了
你又有疑问了:你不是说还没执行到  private static int i = 22; 这一句吗,这就是静态变量会提前加载的缘故了,
                         但是一定注意,提前加载是不是说这句代码不会再执行了吗?不管你怎么理解你记住这句代码  private static int i = 22;  在new Dog()中还没运行,
                         在执行完Animal中的相应的方法后,Dog中会第一个执行这行代码
class Dog extends Animal
           {
              private static int i = 22;         // 第一步
              public Dog()                           // 第二步
                  {                        
                     i = 222;                          // 第三步
                  }
              public void display()              
                  {
                     System.out.println(i);      
                  }

正所谓加载不代表不会运行了,每次实例化的时候仍然会这样的执行一遍,这样的话整个过程是不是更清楚了!
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
基本上各种情况也是涵盖了啊,里面内容不少,值得揣摩,这也是个大知识点!


作者: 王勃    时间: 2012-4-25 17:03
杨国祯 发表于 2012-4-25 16:20
一定要深入理解相关知识,知道你混淆概念了,应该很多人有这个疑问,我有另一个回复,专门介绍执行顺序,有 ...
就是一个静态块,里面有相应的语句,他绝对是第一位加载的,不论你放到哪里这个一定要注意!!

觉得你说的,你说的静态块绝对是第一个加载的,我做了例子,等会我发帖子。你来看。。。这都到第2页,网速不是很快
作者: 黄或俊    时间: 2012-4-25 18:24
王明(1988) 发表于 2012-4-25 09:42
看来见鬼的不是你啊,是你被鬼玩了。哈哈哈哈
我给程序加了注释,你仔细看下啊 ...

还是有点蒙,this这个我自己理解为是:display()被子类覆盖掉了,可以这样理解吗?
但是就是关于i的初始化,感觉结果是零的话又跟毕老师的初始化顺序有冲突
以下个人理解:
   new Dog();
1、加载内存,执行代码块就不说了直到开辟空间分配地址
2、在堆中建立Dog的特有属性,进行默认初始化
3、显示初始化
4、执行构造方法
5、Dog中的第一行有super,所以先执行Animal()
6、Animal()中又用到了子类的display()方法,所以就直接输出子类display()中的语句
【但是这个时候 i 已经显示初始化过了呀!!!】
还没有学过断点调试,所以对于结果纳闷
作者: 王勃    时间: 2012-4-25 18:47
黄或俊 发表于 2012-4-25 18:24
还是有点蒙,this这个我自己理解为是:display()被子类覆盖掉了,可以这样理解吗?
但是就是关于i的初始 ...

你看我的调试图片。。。。就明白了




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