初始化时内存清零
当创建一个对象时,首先将在堆上为这个对象分配足够的空间,这块空间会被清零,即基本类型数据都设置成了缺省值,即数字为0,布尔型为false,字符为空白字符(是一个字符,但不是null),引用设置成null。
public abstract class P {
//在构建子类时先调用父类构造函数
public P() {
//调用子类的方法
f();
}
public abstract void f();
}
class S extends P {
int integer = 11;
double decimal = 0.1;
boolean bool = true;
char character = 'a';
String str = "str";
public S() {
print("子类构造函数调用前");
}
public void f() {
print("子类构造函数调用中");
}
private void print(String msg) {
System.out.println(msg + " integer=" + integer + " decimal=" + decimal
+ " bool=" + bool + " character=" + (int) character + "("
+ character + ")" + " str=" + str);
}
public static void main(String[] args) {
new S();
}
/*
* output:
* 子类构造函数调用中 integer=0 decimal=0.0 bool=false character=0() str=null
* 子类构造函数调用前 integer=11 decimal=0.1 bool=true character=97(a) str=str
*/
}
初始化顺序
在一个类里,属性初始化的顺序是由属性在类内的定义顺序决定的。即使属性定义大量遍布于方法定义的中间,这些属性仍会在调用任何方法之前得到初始化——甚至在构造函数调用之前,如:
class Tag {
Tag(int marker) {
System.out.println("Tag(" + marker + ")");
}
}
class Card {
Tag t1 = new Tag(1); // 定义在构造函数前
Card() {
System.out.println("Card()");
t3 = new Tag(4); // 重新初使化t3
}
Tag t2 = new Tag(2); // 定义在构造函数后
void f() {
System.out.println("f()");
}
Tag t3 = new Tag(3); // 最后
}
public class MainTest {
public static void main(String[] args) {
Card t = new Card();
t.f();
}
/* output:
Tag(1)
Tag(2)
Tag(3)
Card()
Tag(4)
f()
*/
}
但要注意的是,属性之间的引用初始化时,被引用的属性一要在先于引用属性的定义,如:
public class A {
private int g(int ii) {
/*
* 此输入i语句在调用的方法定义在属性变量定义前是可以的,因为属性的
* 初始化一定在调用前完成
*/
System.out.println(i);
return 1;
}
int i = f();
/*
* 注:这一行调用一定要放在i定义后,不然编译时出错,因为引用前一定要先
* 定义
*/
int j = g(i);
private int f() {
return 0;
}
}
注:上面讲的是非静态属性,静态属性也一样,比如将上面的i与j都是定义成静态时与面上规则是一样;但是如果i是静态j为非静态时,静态属性i是可以在引用它的非静态j后面定义的,因为静态属性要先于非静态属性初始(静态与非静态属性混合定义时先初始化静态的,再初始化非静态,后面还进一步讨论);当然不可能j是静态的,而i是非静态的,因为静态的方法或属性不能引用非静态的方法或属性。
上面讨论了非静态属性初始化顺序,下面来看看静态与非静态属行、静态与非静态块还有构造函数一起时初始化顺序:
class P {
int i = 9;
int j;
{
System.out.println("P's block");
}
int y = prt("P.y initialized");
P() {
System.out.println("P()");
prt("i = " + i + ", j = " + j);
j = 39;
}
static {
System.out.println("P's static block");
}
static int x1 = prt("static P.x1 initialized");
static int prt(String s) {
System.out.println(s);
return 47;
}
}
public class S extends P {
int k = prt("S.k initialized");
{
System.out.println("S's block");
}
S() {
System.out.println("S()");
prt("k = " + k);
prt("j = " + j);
}
static int x2 = prt("static S.x2 initialized");
static {
System.out.println("S's static block");
}
static int prt(String s) {
System.out.println(s);
return 63;
}
public static void main(String[] args) {
new S();//①如果注释掉此句,还是会输出下面结果中的前四行
}
/* output:
P's static block
static P.x1 initialized
static S.x2 initialized
S's static block
P's block
P.y initialized
P()
i = 9, j = 0
S.k initialized
S's block
S()
k = 63
j = 39
*/
}
最来后总结一下类的初始化顺序:
父类静态属性(块)初始化->子类静态属性(块)初始化->对象内存清零->父类非静态属性(块)初始化->父类构造函数->子类非静态属性(块)初始化->子类构造函数
注,静态属性与静态块的初始化顺序是按定义的先后顺序初始化,即静态变量和静态初始化块是依照他们在类中的定义顺序进行初始化的;非静态属性与非静态块的初始化也是这样。