在上一篇文章中讲述了java对象的销毁和垃圾回收器的工作方式,这篇文章将讲下java对象的创建,这对我们平时的开发也有一定的帮助。
假如存在一个名为User的类,User的成员变量有静态的和非静态的。
1:当创建User类的对象或调用其静态成员变量或静态方法时,java编译器会先定位User.class
2:java装载机ClassLoader加载User.class,此时会初始化其静态成员变量。因此,静态成员变量只会在Class首次被装载时初始化一次
3:通过new User()创建对象,此时会在堆区域为其分配存储空间
4:该存储空间会被清零,自动为User对象的成员变量设置默认值(基本类型成员变量,如int会被赋值0),如果有引用,设置为null
5:执行成员变量的初始化(例如int times = 9)
6:调用构造方法,完成类的完整初始化
还有一点需要注意,就是出现继承时的构造方法的调用顺序
如果User继承基类BaseBean,那么在创建User对象,上述的第4步走完之后,顺序会发生变化
5:调用基类BaseBean的构造方法
6:执行User成员变量的初始化
7:调用User的构造函数
初始化不当会产生的问题
下面这个程序就是由于构造方法初始化不当引起问题的典型。看下面两个类的定义:
最后是以上程序的执行结果。
分析:在User对象分配堆区域,并对区域清零后,此时成员变量都被赋予了默认值,
1:调用基类构造函数,打印BaseBean
2:调用init()方法,由于基类的此方法被User重写,所以此时调用User对象的init()方法,但此时name变量还未被初始化,只是被赋予了默认值而已,因此打印的是User name:null(此处很惊讶,是造成我们要讨论的问题的原因)
3:完成成员变量初始化和调用User构造函数
问题分析:User子类在未完全初始化的情况下访问其成员变量,造成null的出现
总结
在定义构造函数时,尽量快速完成初始化,尽量不要调用其他方法,因为你不知道子类是否会重写被调用的方法,就像上面的例子,在init()中就会出现null的情况,如果此时直接使用name就会报空指针。如果非要调用方法,可以将方法定义为final 或者private(默认也会被final修饰)类型,避免被子类重写,产生问题。