黑马程序员技术交流社区
标题:
对象创建在内存中的细节问题
[打印本页]
作者:
夏天
时间:
2012-9-5 22:02
标题:
对象创建在内存中的细节问题
本帖最后由 夏诗瑶 于 2012-9-8 00:48 编辑
现在已知子类继承父类,子父类中都有 静态代码块,构造代码块,带参构造函数。
创建一个子类对象 Zi z=new Zi("lisi",20);
这句话,在内存中 是 如下实现的
// 1、因为new用到了Zi.class,并且子类继承了父类。所以会先找到Fu.class,再找到Zi.class文件并加载到内存中
// 2、执行 父类静态代码块
// 3、执行子类静态代码块
// 4、在堆内存中开辟空间,分配内存地址
// 5、在堆内存中建立对象的特有属性,并进行默认初始化
// 6、对属性显式初始化
// 7、子类构造代码块
// 8、子类构造函数
// 9、将内存地址付给栈内存中的z变量
现在问题是:
//a父类构造代码块
//b父类构造函数
ab 应该分别放在哪一步之前,哪一步之后?
作者:
孙岳
时间:
2012-9-5 22:09
// 1、因为new用到了Zi.class,并且子类继承了父类。所以会先找到Fu.class,再找到Zi.class文件并加载到内存中
// 2、执行 父类静态代码块
// 3、执行子类静态代码块
//a父类构造代码块
//b父类构造函数
// 4、在堆内存中开辟空间,分配内存地址
// 5、在堆内存中建立对象的特有属性,并进行默认初始化
// 6、对属性显式初始化
// 7、子类构造代码块
// 8、子类构造函数
// 9、将内存地址付给栈内存中的z变量
作者:
黑马--张帅
时间:
2012-9-5 22:12
本帖最后由 黑马--张帅 于 2012-9-5 22:41 编辑
a 父类构造代码块,b父类构造函数 对于这两步:因为你在子父类中都定义构造代码块和带参的构造函数,而现在你定义了一个子类对象,而在进行子类对象初始化时,子类都会调用父类的构造函数super()这句话一般是隐式的。而现在你的程序中没有定义无参构造函数,所以子类对象进行初始化时要显式定义父类构造函数super(s)。综上所述:在子类对象初始化时,都是父类构造函数先执行。然而构造代码块是对构造函数进行初始化的,所以a必定在b之前
// 1、因为new用到了Zi.class,并且子类继承了父类。所以会先找到Fu.class,再找到Zi.class文件并加载到内存中
// 2、执行 父类静态代码块
// 3、执行子类静态代码块
// 4、在堆内存中开辟空间,分配内存地址
// 5、在堆内存中建立对象的特有属性,并进行默认初始化
// 6、对属性显式初始化
//a父类构造代码块
//b父类构造函数
// 7、子类构造代码块
// 8、子类构造函数
// 9、将内存地址付给栈内存中的z变量
作者:
田旭阳
时间:
2012-9-5 22:18
本帖最后由 田旭阳 于 2012-9-5 22:39 编辑
1.class Parent{
2. static String name = "hello";
3. static {
4. System.out.println("parent static block");
5. }
6. {
7. System.out.println("parent block");
8. }
9. public Parent(){
10. System.out.println("parent constructor");
11. }
12.}
13.
14.class Child extends Parent{
15. static String childName = "hello";
16. static {
17. System.out.println("child static block");
18. }
19. {
20. System.out.println("child block");
21. }
22. public Child(){
23. System.out.println("child constructor");
24. }
25.}
26.
27.public class StaticIniBlockOrderTest {
28.
29. public static void main(String[] args) {
30. new Child();//语句(*)
31. }
32.}
当执行完语句(*)时,打印结果是这样一个顺序:parent static block,child static block,parent block,parent constructor,child block,child constructor。
分析:当执行new Child()时,它首先去看父类里面有没有静态代码块,如果有,它先去执行父类里面静态代码块里面的内容,当父类的静态代码块里面的内容执行完毕之后,接着去执行子类(自己这个类)里面的静态代码块,当子类的静态代码块执行完毕之后,它接着又去看父类有没有非静态代码块,如果有就执行父类的非静态代码块,父类的非静态代码块执行完毕,接着执行父类的构造方法;父类的构造方法执行完毕之后,它接着去看子类有没有非静态代码块,如果有就执行子类的非静态代码块。子类的非静态代码块执行完毕再去执行子类的构造方法,这个就是一个对象的初始化顺序。
总结:对象的初始化顺序:首先执行父类静态的内容,父类静态的内容执行完毕后,接着去执行子类的静态的内容,当子类的静态内容执行完毕之后,再去看父类有没有非静态代码块,如果有就执行父类的非静态代码块,父类的非静态代码块执行完毕,接着执行父类的构造方法;父类的构造方法执行完毕之后,它接着去看子类有没有非静态代码块,如果有就执行子类的非静态代码块。子类的非静态代码块执行完毕再去执行子类的构造方法。总之一句话,静态代码块内容先执行,接着执行父类非静态代码块和构造方法,然后执行子类非静态代码块和构造方法。
父类Static->子类static->父类缺省{}->父类构造函数->子类缺省{}->子类构造函数
(静态变量、静态初始化块)>(变量、初始化块)>构造器
注意:子类的构造方法,不管这个构造方法带不带参数,默认的它都会先去寻找父类的不带参数的构造方法。如果父类没有不带参数的构造方法,那么子类必须用supper关键子来调用父类带参数的构造方法,否则编译不能通过。
// 1、因为new用到了Zi.class,并且子类继承了父类。所以会先找到Fu.class,再找到Zi.class文件并加载到内存中
// 2、执行 父类静态代码块
// 3、执行子类静态代码块
//a父类构造代码块
//b父类构造函数
// 4、在堆内存中开辟空间,分配内存地址
// 5、在堆内存中建立对象的特有属性,并进行默认初始化
// 6、对属性显式初始化
// 7、子类构造代码块
// 8、子类构造函数
// 9、将内存地址付给栈内存中的z变量
作者:
夏天
时间:
2012-9-5 22:29
黑马--张帅 发表于 2012-9-5 22:12
a 父类构造代码块,b父类构造函数 对于这两步:因为你在子父类中都定义构造代码块和带参的构造函数,而现 ...
ab要插入上面的 顺序中。。。
作者:
郭阳
时间:
2012-9-5 22:37
本帖最后由 郭阳 于 2012-9-5 22:45 编辑
顺序应该为
// 1、因为new用到了Zi.class,并且子类继承了父类。所以会先找到Fu.class,再找到Zi.class文件并加载到内存中
// 2、执行 父类静态代码块
// 3、执行子类静态代码块
// 4、在堆内存中开辟空间,分配内存地址
// 5、在堆内存中建立对象的特有属性,并进行默认初始化
// 6、对属性显式初始化
//a父类构造代码块
// 7、子类构造代码块
// 8、子类构造函数
//b父类构造函数
// 9、将内存地址付给栈内存中的z变量
构造代码块是对整个对象进行初始化,构造函数是对对象特定成员进行初始化,所以先是构造代码块,后是构造函数
构造函数,第一行都是super();或者this();**而this();最终也会被引向父类,所以父类的构造函数会先于子类的进行。
至于访问父类哪一个构造函数那就是你自己定义的了。
而构造代码块自然也是要先运行父类的。
所以顺序如上
作者:
夏天
时间:
2012-9-8 00:47
问题已解决,答案应该是 4,a,b,5
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/)
黑马程序员IT技术论坛 X3.2