Java里有局部變量、實(shí)例變量、靜態(tài)變量,它們的初始化表現(xiàn)也不盡相同。下面來(lái)總結(jié)一下。
局部變量
void f() {
int i;
i++; // Error -- i not initialized
}
局部變量應(yīng)該是最簡(jiǎn)單的情景,在一個(gè)方法內(nèi)定義一個(gè)變量,如果不為它賦予初始值,編譯器則會(huì)報(bào)錯(cuò)。所以聲明局部變量時(shí)必須同時(shí)賦予其初始值。
實(shí)例變量
每一個(gè)實(shí)例變量都會(huì)保證有一個(gè)初始值。
public class InitialiValues {
boolean t;
char c;
byte b;
short s;
int i;
long l;
float f;
double d;
InitialiValues initialization;
void printInitialValues() {
System.out.println("boolean " + t);
System.out.println("char " + c + "");
System.out.println("byte " + b);
System.out.println("short " + s);
System.out.println("int " + i);
System.out.println("long " + l);
System.out.println("float " + f);
System.out.println("double " + d);
}
public static void main(String[] args) {
new InitialiValues().printInitialValues();
}
}
/* Output */
boolean false
char null
byte 0
short 0
int 0
long 0
float 0.0
double 0.0
reference null
即使不指定初始值,實(shí)例變量會(huì)自動(dòng)擁有一個(gè)默認(rèn)初始值
構(gòu)造器初始化
public class Counter {
int i;
public Counter() {
System.out.println("before: " + i);
i = 7;
System.out.println("after: " + i);
}
public static void main(String[] args) {
new Counter();
}
}
//Output:
before: 0
after: 7
我們可以利用構(gòu)造器來(lái)進(jìn)行初始化,但是在構(gòu)造器進(jìn)行初始化之前,counter已經(jīng)自動(dòng)初始化了一次。
初始化順序
在一個(gè)類中,成員變量的初始化順序是由變量在類中定義的順序決定的,而且總會(huì)在類中的任何方法被調(diào)用前就已經(jīng)初始化好了。
public class Window {
Window(int marker) {
System.out.println("Window(" + marker + ")");
}
}
public class House {
Window w1 = new Window(1);
House() {
System.out.println("House()");
w3 = new Window(33);
}
Window w2 = new Window(2);
void f() {
System.out.println("f()");
}
Window w3 = new Window(3);
}
public static void main(String[] args) {
House h = new House();
h.f();
}
//Output:
Window(1)
Window(2)
Window(3)
House()
Window(33)
f()
可以看到,雖然Window對(duì)象的定義被打散了,但確實(shí)是等到所有的Windown變量初始化完成后,才執(zhí)行構(gòu)造器。
繼承關(guān)系下的初始化順序
在上面的基礎(chǔ)上加上一個(gè)子類繼承關(guān)系后,情況又會(huì)怎樣?
public class SonHouse extends House{
Window sonWindow = new Window(4);
SonHouse() {
sonWindow = new Window(44);
}
}
}
public static void main(String[] args) {
House h = new SonHouse();
h.f();
}
//Output:
Window(1)
Window(2)
Window(3)
House()
Window(33)
Window(4)
Window(44)
f()
事實(shí)證明,孝順是一種優(yōu)良品質(zhì),先讓父類初始化完成后,再輪到子類
static變量的初始化時(shí)機(jī)
public class Bowl {
Bowl(int marker) {
System.out.println("Bowl(" + marker + ")");
}
void f1(int marker) {
System.out.println("f1(" + marker + ")");
}
}
public class Table {
static Bowl bowl1 = new Bowl(1);
Table() {
System.out.println("Table()");
bowl2.f1(1);
}
void f2(int marker) {
System.out.println("f2(" + marker + ")");
}
static Bowl bowl2 = new Bowl(2);
}
public class Cupboard {
Bowl bowl3 = new Bowl(3);
static Bowl bowl4 = new Bowl(4);
Cupboard() {
System.out.println("Cupboard");
bowl4.f1(2);
}
void f3(int marker) {
System.out.println("f3(" + marker + ")");
}
static Bowl bowl5 = new Bowl(5);
}
public class StaticInitialization {
public static void main(String[] args) {
System.out.println("Creating new Cupboard() in main");
new Cupboard();
System.out.println("Creating new Cupboard() in main");
new Cupboard();
table.f2(1);
cupboard.f3(2);
}
static Table table = new Table();
static Cupboard cupboard = new Cupboard();
}
//Output:
Bowl(1)
Bowl(2)
Table()
f1(1)
Bowl(4)
Bowl(5)
Bowl(3)
Cupboard
f1(2)
Creating new Cupboard() in main
Bowl(3)
Cupboard
f1(2)
Creating new Cupboard() in main
Bowl(3)
Cupboard
f1(2)
f2(1)
f3(2)
可以看出,static變量總是最先初始化。要注意的是,static變量只會(huì)初始化一次,而且只會(huì)在第一次需要用到的時(shí)候才進(jìn)行
初始化。然后才到 non-static變量初始化
就像上述示例,要執(zhí)行main(),StaticInitialization類就需要被加載,然后其static變量 table和cupboad這個(gè)時(shí)候
同樣需要被加載上,又因?yàn)樗鼈兌加衧tatic Bowl 變量,所以這是Bowl類也同時(shí)被加載。所以這些類在執(zhí)行main方法前都已經(jīng)
加載好了。
其實(shí),學(xué)習(xí)過(guò)JVM話就會(huì)知道,一個(gè)class只有在第一次需要引用的時(shí)候才會(huì)被加載,以后需要用到這個(gè)class就直接從classloader
獲取不需要加載了。而 static變量的初始化就是在class第一次被加載到classloader的時(shí)候發(fā)生的。所以不難理解為什么static變量
只會(huì)初始化一次。
總結(jié)
對(duì)于實(shí)例變量,按照聲明的順序逐個(gè)初始化,先父類,后子類
static 成員在類第一次被加載時(shí)進(jìn)行初始化,并且只初始化這么一次