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

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 高原 中级黑马   /  2012-7-7 17:23  /  3105 人查看  /  9 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 高原 于 2012-7-7 23:30 编辑

毕老师第六天的课程中讲到对象的初始化过程。他说:先对属性显示初始化,然后再进行构造代码块初始化。
我写了个程序,与老师说的不太一样。
  1. public class Person {
  2.         
  3.         {
  4.                 System.out.println("name=  " + this.name);
  5.         }
  6.         public String name = "zhangsan";
  7.         
  8.         static {
  9.                 Person.country = "static country";
  10.                 System.out.println("country= " + Person.country);
  11.         }
  12.         public static String country = "cn";

  13.         public Person(String name) {
  14.                 this.name = name;
  15.         }
  16. }
  17. public class TestInit {
  18.         public static void main(String[] args) {
  19.                 Person p = new Person("lisi");
  20.                 System.out.println("name= " + p.name);
  21.                 System.out.println("country= " + Person.country);
  22.         }
  23. }
复制代码
打印结果为:
country= static country
name=  null
name= lisi
country= cn
这说明构造代码块先执行了,然后显示初始化才执行。
并且,静态代码块的执行时机也比类变量的显示初始化要早。
所以构造代码块,显示初始化谁在代码中比较靠前,谁就先执行,静态代码块和类变量显示初始化也是同样的。
我觉得对象初始化过程是这样的:

1.       首先加载类到内存中
2.       对于类变量,系统默认初始化为null或0等默认值
3.       执行静态代码块或类变量的显示初始化(代码中谁在前面谁就先执行)
4.       在堆内存中开辟空间,分配内存地址
5.       对于成员变量,首先系统默认初始化为null或者0等默认值
6.       然后执行构造代码块或成员变量显示初始化(代码中谁在前面谁就先执行)
7.       最后才是构造方法初始化
8.       将堆内存中的地址付给栈内存中的引用

大家怎样认为?



评分

参与人数 1技术分 +1 收起 理由
韦念欣 + 1 高原兄弟敢于挑战老师,真的很给力!.

查看全部评分

9 个回复

倒序浏览
本帖最后由 邵阳 于 2012-7-7 20:50 编辑

我不认为楼主是正确的,恰恰相反我认为楼主的代码正好印证了毕老师的说法,我慢慢解释。对象的初始化过程如下:
1:因为new用到了Person.class,所以会先找到Person.class文件并加载到内存中。
2:执行该类型中的static代码块,如果有的话,给Person.class类进行初始化。
3:在对内存中开辟空间,分配内存地址。
4:在堆内存中建立对象的特有属性,并进行默认初始化。
5:对属性显示初始化。
6:对对象进行构造代码块初始化。
7:对对象进行相应的构造函数初始化。
8:将内存地址付给栈内存正的p变量。


public class Person {
        
        {
                System.out.println("name=  " + this.name);//2:建立对象,默认初始化为null
        }
        public String name = "zhangsan";
        
        static {       //   1:首先执行static代码块里面的内容,打印结果是country= static country

                Person.country = "static country";
                System.out.println("country= " + Person.country);
        }
        public static String country = "cn";//4:虽然对象一加载内存,都已经初始化。但是其
                                                                                      //System.out.println("country= " + Person.country);放在最后,所以最后执行。

       public Person(String name) {  //3:构造函数初始化。
                this.name = name;
        }
}
public class TestInit {
        public static void main(String[] args) {
                Person p = new Person("lisi");
                System.out.println("name= " + p.name);
                System.out.println("country= " + Person.country);
        }
}
1:country= static country
2:name=  null
3:name= lisi
4:country= cn

解释的有点牵强,我尽力啦,以后还会继续补充。
我现在搞懂啦,毕老师少说了一条,因为他讲的时候,初始变量在构造代码块的上面。
7楼同学说的很对
6. 执行构造代码块或成员变量显示初始化(代码中谁在前面谁就先执行)
所以很佩服楼主的挑战权威的精神,
强烈版主建议给楼主加3分,七楼同学加2分

点评

高原同学和曹恒业同学表现的很不错,我也想多加几分给他们,但是管理员下达的命令,……,不过其他版主觉得不错的话也是可以再加...  发表于 2012-7-8 00:38

评分

参与人数 1技术分 +1 收起 理由
韦念欣 + 1 赞一个!

查看全部评分

回复 使用道具 举报
以前有人回答过这个问题 我中介了一下 :
1,静态代码块、构造代码块和构造函数的区别
                静态代码块:用于给类初始化,类加载时就会被加载执行,只加载一次。
                构造代码块:用于给对象初始化的。只要建立对象该部分就会被执行,且优先于构造函数。
                构造函数:  给对应对象初始化的,建立对象时,选择相应的构造函数初始化对象。               
          创建对象时,三者被加载执行顺序:静态代码块--->构造代码块--->构造函数


2,什么时候会加载类?
                使用到类中的内容时加载:有三种情况
                                1.创建对象:new StaticCode();
                                2.使用类中的静态成员:StaticCode.num=9;  StaticCode.show();
                                3.在命令行中运行:java StaticCodeDemo

3,类所有内容加载顺序和内存中的存放位置:
        利用语句进行分析。
                1.Person p=new Person("zhangsan",20);
                        该句话所做的事情:
                        1.在栈内存中,开辟main函数的空间,建立main函数的变量 p。
                        2.加载类文件:因为new要用到Person.class,所以要先从硬盘中找到Person.class类文件,并加载到内存中。
                                加载类文件时,除了非静态成员变量(对象的特有属性)不会被加载,其它的都会被加载。
                                记住:加载,是将类文件中的一行行内容存放到了内存当中,并不会执行任何语句。---->加载时期,即使有输出语句也不会执行。
                                        静态成员变量(类变量)                ----->方法区的静态部分
                                        静态方法                              ----->方法区的静态部分
                                        非静态方法(包括构造函数)        ----->方法区的非静态部分
                                        静态代码块                                        ----->方法区的静态部分
                                        构造代码块                                        ----->方法区的静态部分
                                       
                                        注意:在Person.class文件加载时,静态方法和非静态方法都会加载到方法区中,只不过要调用到非静态方法时需要先实例化一个对象
                                        ,对象才能调用非静态方法。如果让类中所有的非静态方法都随着对象的实例化而建立一次,那么会大量消耗内存资源,
                                        所以才会让所有对象共享这些非静态方法,然后用this关键字指向调用非静态方法的对象。
      
                       
                        3.执行类中的静态代码块:如果有的话,对Person.class类进行初始化。
                        4.开辟空间:在堆内存中开辟空间,分配内存地址。
                        5.默认初始化:在堆内存中建立 对象的特有属性,并进行默认初始化。
                        6.显示初始化:对属性进行显示初始化。
                        7.构造代码块:执行类中的构造代码块,对对象进行构造代码块初始化。
                        8.构造函数初始化:对对象进行对应的构造函数初始化。
                        9.将内存地址赋值给栈内存中的变量p。
                2.p.setName("lisi");
                        1.在栈内存中开辟setName方法的空间,里面有:对象的引用this,临时变量name
                        2.将p的值赋值给this,this就指向了堆中调用该方法的对象。
                        3.将"lisi" 赋值给临时变量name。
                        4.将临时变量的值赋值给this的name。
                3.Person.showCountry();      
                        1.在栈内存中,开辟showCountry()方法的空间,里面有:类名的引用Person。
                        2.Person指向方法区中Person类的静态方法区的地址。
                        3.调用静态方法区中的country,并输出。
           注意:要想使用类中的成员,必须调用。通过什么调用?有:类名、this、super

评分

参与人数 1技术分 +1 收起 理由
黑马张扬 + 1

查看全部评分

回复 使用道具 举报
邵阳 发表于 2012-7-7 17:42
我不认为楼主是正确的,恰恰相反我认为楼主的代码正好印证了毕老师的说法,我慢慢解释。对象的初始化过程如 ...

我也有点疑问

System.out.println("name=  " + this.name);
这是在构造代码块里面的
为什么显示null?

如果属性的显式初始化构造代码块之前
那么 public String name = "zhangsan";  先执行
这个name属性现在应该是"zhangsan"了

属性是初始化了 但是没有显式赋的初值
这是不是对"属性的显式初始化"理解的问题?
回复 使用道具 举报
温少邦 发表于 2012-7-7 18:27
我也有点疑问

System.out.println("name=  " + this.name);


这里我也没有理解很深刻,不过我死记硬背记着啦。我啥时候想明白了继续补充
回复 使用道具 举报
温少邦 发表于 2012-7-7 18:27
我也有点疑问

System.out.println("name=  " + this.name);

3楼说的很清楚了

我简单说一下我的理解简化后的执行过程

1、new一个对象的时候,就开始加载静态部分了,先执行静态方法static{}、静态static String country属性部分,然后执行静态代码块{System.out.println(("name=  " + this.name);}部分
2、new Person("lisi")这里,调用构造函数,Person(String name),这个时候就 p这个对象变量的name属性就被赋值了。
3、然后依次执行main函数中打印部分,就依次输出控制台显示的结果了
回复 使用道具 举报
本帖最后由 曹恒业 于 2012-7-7 20:38 编辑
  1. public class Person {
  2.         
  3.         {
  4.                 System.out.println("name=  " + this.name);
  5.         }
  6.         public String name = "zhangsan";          //如果将这个对name的显示初始化放到构造代码块之前,那么就符合了毕老师视频里的结论。最后name打印的结果就是“zhangsan”。若有怀疑,大家可以试试,亲自动手,会印象更深刻。
  7.         
  8.         static {
  9.                 Person.country = "static country";
  10.                 System.out.println("country= " + Person.country);
  11.         }
  12.         public static String country = "cn";

  13.         public Person(String name) {
  14.                 this.name = name;
  15.         }
  16. }
  17. public class TestInit {
  18.         public static void main(String[] args) {
  19.                 Person p = new Person("lisi");
  20.                 System.out.println("name= " + p.name);
  21.                 System.out.println("country= " + Person.country);
  22.         }
  23. }
复制代码
首先,感谢楼主为我们发现了这么一个容易让大家忽视的细节,确实,一开始我也不明白结果为何和毕老师说的不同,但我最终还是决定相信JVM。因此自己写代码试验了良久,正好把全部关于初始化的顺序都做了实验。终于确定了LZ的猜想。
6.       执行构造代码块或成员变量显示初始化(代码中谁在前面谁就先执行) 这里的详细解释见代码中注释。

评分

参与人数 1技术分 +1 收起 理由
韦念欣 + 1 这位兄弟的代码很给力!

查看全部评分

回复 使用道具 举报
曹恒业 发表于 2012-7-7 20:23
首先,感谢楼主为我们发现了这么一个容易让大家忽视的细节,确实,一开始我也不明白结果为何和毕老师说的不 ...

你的很对啊
回复 使用道具 举报
邵阳 发表于 2012-7-7 17:42
我不认为楼主是正确的,恰恰相反我认为楼主的代码正好印证了毕老师的说法,我慢慢解释。对象的初始化过程如 ...

哥们你太帅了,我感动了,谢谢啊!!!
希望斑竹能加分啊
回复 使用道具 举报
曹恒业 发表于 2012-7-7 20:23
首先,感谢楼主为我们发现了这么一个容易让大家忽视的细节,确实,一开始我也不明白结果为何和毕老师说的不 ...

哈哈,过奖了,我也是一时好奇才做了这个实验~:)
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马