黑马程序员技术交流社区

标题: 小程序的疑惑 [打印本页]

作者: 蔚蓝小嗨    时间: 2013-6-9 15:12
标题: 小程序的疑惑
本帖最后由 蔚蓝小嗨 于 2013-6-10 10:29 编辑

class X {
        Y b = new Y();
        X() {
                        System.out.print("X");
        }
}

class Y {
        Y() {
                        System.out.print("Y");
        }
}

public class Test extends X {
        Y y = new Y();
        Test() {
                        System.out.print("Z");
        }

        public static void main(String[] args) {
                        new Test();
        }
}
这个程序的输出结果是什么,为什么


作者: 刘凯    时间: 2013-6-9 15:48
这个设计到java语法问题,是个面试小题  ,我就不答了,等后边给分好了 ,  需要讲清楚哦!
作者: 小冰块    时间: 2013-6-9 16:09
答案是YXYZ,请看代码里的注释:

  1. class X
  2. {      
  3.         Y b = new Y();// 【2】父类在调用构造函数之前会首先加载自己的成员变量,这里打印第一个Y

  4.     X()
  5.         {
  6.         System.out.print("X"); //【3】调用父类构造函数,打印X
  7.     }
  8. }

  9. class Y
  10. {        
  11.         Y()
  12.         {
  13.         System.out.print("Y");
  14.     }
  15. }


  16. public class Test extends X {
  17.        Y y = new Y();// 3
  18.          
  19.         Test()
  20.         {                       //【1】首先从这里开始走,这里默认有一个super(),所以请跳转到父类中
  21.                                 //【4】再加载本类的成员函数,因为构造函数作用就是给成员初始化,这里是第二个Y
  22.                System.out.print("Z");//【5】这里打印最后一个字母Z
  23.         }

  24.         public static void main(String[] args)
  25.                 {
  26.            new Test();//【0】这里是程序入口,调用了本类构造函数,请移步本类构造函数
  27.         }
  28. }
复制代码

作者: 小冰块    时间: 2013-6-9 16:16
小冰块 发表于 2013-6-9 16:09
答案是YXYZ,请看代码里的注释:

;P有奖励不?
作者: 刘凯    时间: 2013-6-9 16:17
小冰块 发表于 2013-6-9 16:16
有奖励不?

必须啊 ,,,  要汉子么 {:3_52:}
作者: 小冰块    时间: 2013-6-9 16:28
刘凯 发表于 2013-6-9 16:17
必须啊 ,,,  要汉子么

汉子啊,我的确木有呢~
毕姥爷在说面向对象的时候有举例子说过,他老人家是这么说的:

Person p = newPerson();这句话做了什么事?

1、因为new用到了person.class,所以会先找到Person.class文件并加载到内存中。
2、执行该类中的static代码块,如果有的话,给person.class类进行初始化。
3、在堆内存中开辟空间,分配内存地址。
4、在堆内存中建立对象的特有属性并进行默认初始化。
5、对属性进行显示初始化。
6、对对象进行构造代码块构造初始化。
7、对对象进行构造代码函数构造初始化。
8、 将内存地址赋给栈内存中的p变量。

如果说普通程序加载顺序的话,应该是这样:
1、首先加载静态成员/静态代码块,如果有调用静态方法,就先加载静态方法。如果有父类,那么就先加载父类
2、再加载非静态成员/代码块,如果有父类,同上(ps:实例块要在创建时才加载)
3、调用构造方法,如果有父类,同上

好像大概就是这样……

作者: w270307032    时间: 2013-6-9 16:31
楼上的正解,我只想补充2点:
1
Y y = new Y();// 3
         
        Test()
        {                       //【1】首先从这里开始走,这里默认有一个super(),所以请跳转到父类中话后
                                //【4】再加载本类的成员函数,因为构造函数作用就是给成员初始化,这里是第二个Y
楼上【4】标错位置了,应该放在Y y = new Y();// 3这句后
2.
System.out.print("X"); //【3】调用父类构造函数,打印X
这里加载完父类后,调用的是父类在子类super()指定的父类的对应构造函数,没指定的话,默认的是无参的,如果父类构造函数有重载形式,调用的就就看子类的super怎么指定了。
作者: 小冰块    时间: 2013-6-9 16:31
啊啊啊!!我又逛了一下午论坛,我的进度!!{:soso_e109:}
作者: 张歆明    时间: 2013-6-9 16:34
答案是:XYXZ
要构造一个某类的对象  就必须执行完所有的初始化之后再调用相应的构造函数  

简单说:( 没有继承的时候)
new Person();一执行:
实例化过程是:静态代码块,静态成员初始化(第一次)

成员默认初始化-->成员显示初始化-->构造代码块初始化-->构造方法 这个顺序

继承的时候:
调用子类构造方法之后 执行顺序如下:
(结论)
父类static代码块(仅执行一次)--->子类静态代码块(仅执行一次)
               ---->父类(父类成员隐式初始化--->父类成员显式初始化--->父类构造代码块 --->父类构造方法)
               ---->子类(子类成员隐式初始化--->子类成员显式初始化--->子类构造代码块 --->子类构造方法)
new Test();
构造子类  先构造父类 发现父类有显式初始化 Y =new Y(); ---->输出Y
再执行父类构造函数 输出X
此时回到子类 本想执行子类构造方法Test() 但是发现子类也有成员变量显式初始化 Y=new Y(); -->再次输出Y
最够进入子类Test的构造方法 --->输出Z


所以 Y X Y Z


***************************************、
最后做了一个实验  验证一下 子类 父类  都有 构造方法  构造代码块  静态代码块  显式初始化的时候 执行顺序
  1. package com.thread;

  2. public class Test2 extends X{
  3.         static{
  4.          System.out.println("..Test2 Static Block..");
  5.         }
  6.        
  7.     Y y = new Y();
  8.     {
  9.             System.out.print("..Test2 Block..");
  10.     }
  11.    
  12.     Test2() {
  13.                     System.out.print("Z");
  14.     }

  15.     public static void main(String[] args) {
  16.                     new Test2();
  17.                     System.out.println();
  18.                     System.out.println("---------------");
  19.                     System.out.println("第二次调用");
  20.                     new Test2();
  21.     }
  22. }

  23. class X {
  24.         static{
  25.                 System.out.print("--X Static Block--");
  26.         }
  27.         Y b = new Y();
  28.         //构造代码块
  29.         {
  30.                 System.out.print("**X Block**");
  31.         }
  32.         X() {
  33.                         System.out.print("X");
  34.         }
  35. }

  36. class Y {
  37.         Y() {
  38.                         System.out.print("Y");
  39.         }
  40. }
复制代码
运行结果:
**********************************
--X Static Block--..Test2 Static Block..
Y**X Block**XY..Test2 Block..Z
---------------
第二次调用
Y**X Block**XY..Test2 Block..Z

**********************************



作者: 刘凯    时间: 2013-6-9 16:49
小冰块 发表于 2013-6-9 16:28
汉子啊,我的确木有呢~
毕姥爷在说面向对象的时候有举例子说过,他老人家是这么说的:

基本上没啥问题。

简要点讲就是  执行顺序为:

1静态代码块
2成员默认初始化
3成员显示初始化
4构造代码块
5构造函数

其中  静态代码块 只在加载类时运行一次   构造代码块 创建一次对象就运行一次   

如果有父类,再创建子类对象时  须知道父类是怎么样的,所以需要先加载父类  顺序如上
加载完父类以后 在加载子类 顺序如上

作者: 刘凯    时间: 2013-6-9 16:53
小冰块 发表于 2013-6-9 16:31
啊啊啊!!我又逛了一下午论坛,我的进度!!

{:3_52:}  加油啊    21期需要妹子!!
作者: 小冰块    时间: 2013-6-9 16:55
刘凯 发表于 2013-6-9 16:49
基本上没啥问题。

简要点讲就是  执行顺序为:

成员初始化就是初始化,什么叫默认初始化和显示初始化??
作者: 小冰块    时间: 2013-6-9 16:57
刘凯 发表于 2013-6-9 16:53
加油啊    21期需要妹子!!

我……我估计是赶不上21期了,我六一儿童节那天才发现的黑马:L
作者: 刘凯    时间: 2013-6-9 17:02
小冰块 发表于 2013-6-9 16:55
成员初始化就是初始化,什么叫默认初始化和显示初始化??

比如说  int i;  没有赋值  他的默认值就是0 ;  int i; 那么 先默认初始化 i=0; i=1; 然后显示初始化 i=1; (我这么理解 int i = 1;也是同样的过程 先先默认初始化 i=0;然后再显示初始化 i=1;可能有异议)   不过这个在类中公共变量的时候才会有默认值 ,方法中的局部变量 是不会有默认值的,记着以前demo试验过;
作者: 小冰块    时间: 2013-6-9 17:04
刘凯 发表于 2013-6-9 17:02
比如说  int i;  没有赋值  他的默认值就是0 ;  int i; 那么 先默认初始化 i=0; i=1; 然后显示初始化  ...

明白了,3Q~我去试试~




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