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

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

周毅中

  • 黑马币:

  • 帖子:

  • 精华:

罗旭维 发表于 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,冲突了。
输出结果:
回复 使用道具 举报
关键在于java虚函数的机制,子类重新定义了display方法,用多态的机制解释,当创建的是子类对象那这个display调用的就是子类定义的display。
实际可以理解为子类的方法覆盖(替换)了父类的方法,没有产生新的方法。而声明改成private后,私有成员不能外界(包括子类)访问,所以子类虽然重新定义了display方法,但他没有覆盖(替换)掉原来的display方法,而是产生了一个新的方法。

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

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

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

这些是基础知识。
回复 使用道具 举报
罗旭维 发表于 2012-4-25 12:36
这位同学,我不觉得我哪里有问题,我这边运行没问题,用的是1.6的jdk。

我copy你的代码,只改了个2,就这样,你仔细看看。。。
不是你见鬼了,就是我见鬼了。。。
回复 使用道具 举报
罗旭维 发表于 2012-4-25 12:36
这位同学,我不觉得我哪里有问题,我这边运行没问题,用的是1.6的jdk。

我的意思主要是,你用多态来解释是错误的,这里还未曾体现出多态。。。
仔细想想哦。。。
回复 使用道具 举报
王明(1988) 发表于 2012-4-25 12:42
我的意思主要是,你用多态来解释是错误的,这里还未曾体现出多态。。。
仔细想想哦。。。 ...

受够了,好吧,我无语。
回复 使用道具 举报
本帖最后由 王明(1988) 于 2012-4-25 14:30 编辑
罗旭维 发表于 2012-4-25 12:44
受够了,好吧,我无语。

额,我运行n遍,还是上面的结果。。。
回复 使用道具 举报
王明(1988) 发表于 2012-4-25 12:24
你试试吧,虽说子类访问父类的private 方法不行,我在构造对象时就调用,你觉得我就不能调用了吗?
那你 ...

我还是提醒下你吧,如果你运行的是你贴出来的代码,那请注意你的代码,你测试新建了Dog2类但在main函数里你new 的是Dog类!
回复 使用道具 举报
罗旭维 发表于 2012-4-25 13:04
我还是提醒下你吧,如果你运行的是你贴出来的代码,那请注意你的代码,你测试新建了Dog2类但在main函数里 ...

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

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

再次表示,罗同学很细心啊,谢谢:'(
回复 使用道具 举报
本帖最后由 杨国祯 于 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的值是多少,是不是系统默认提供的!
                  }
           }


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

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



回复 使用道具 举报
本帖最后由 王明(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-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 16:20
一定要深入理解相关知识,知道你混淆概念了,应该很多人有这个疑问,我有另一个回复,专门介绍执行顺序,有 ...
就是一个静态块,里面有相应的语句,他绝对是第一位加载的,不论你放到哪里这个一定要注意!!

觉得你说的,你说的静态块绝对是第一个加载的,我做了例子,等会我发帖子。你来看。。。这都到第2页,网速不是很快
回复 使用道具 举报
王明(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:24
还是有点蒙,this这个我自己理解为是:display()被子类覆盖掉了,可以这样理解吗?
但是就是关于i的初始 ...

你看我的调试图片。。。。就明白了
回复 使用道具 举报
12
您需要登录后才可以回帖 登录 | 加入黑马