初始化塊
Java使用構造器來對單個對象進行初始化操作,使用構造器先完成整個Java對象的狀態初始化,然後將Java對象返回給程序,從而讓該Java對象的信息更加完整,與構造器作用類似的是初始化塊,它也可以對Java對象進行初始化。
初始化塊是Java類裏克出現的第四種成員(成員變量,方法和構造器),一個類裏有多個初始化塊,相同類型的初始化塊之間有順序:先定義的初始化塊先執行,後定義的後執行
[修飾符]{
//初始化塊的可執行性代碼
...
}
初始化塊的修飾符如果有則只能是static,這樣的初始化塊稱爲靜態初始化塊,修飾符可以沒有
可執行性代碼可以包含定義局部變量、調用其他對象的方法,以及使用分支、循環語句等任何可執行性語句
public class Person
{
// 下面定義一個實例初始化塊
{
var a = 6;
if (a > 4)
{
System.out.println("Person實例初始化塊:局部變量a的值大於4");
}
System.out.println("Person的實例初始化塊");
}
// 定義第二個實例初始化塊
{
System.out.println("Person的第二個實例初始化塊");
}
// 定義無參數的構造器
public Person()
{
System.out.println("Person類的無參數構造器");
}
public static void main(String[] args)
{
new Person();
}
}
執行結果如下:
Person初始化塊: 局部變量a的值大於4
Person的初始化塊
Person的第二個初始化塊
Person類的無參數構造器
- 系統總是先調用該類裏定義的初始化塊,如果一個類裏定義了兩個普通初始化塊,則先定義的先執行後定義的後執行
- 初始化塊雖然是Java類的一種成員,但是他沒有名字,沒有標識,所以無法通過類、對象來調用 ,只在創建Java對象時隱式的執行,並且在執行構造器之前執行
- 定義多個初始化塊實際上沒有任何意義,因爲他們總是隱式的執行而且全部執行,完全可以把多個初始化塊合併成爲一個
普通的初始化塊、聲明實例變量指定的默認值都可以認爲是對象的初始化代碼,他們的執行順序與源程序中的排列順序相同
public class InstanceInitTest
{
// 先執行實例初始化塊將a實例變量賦值爲6
{
a = 6;
}
// 再執行將a實例變量賦值爲9
int a = 9;
public static void main(String[] args)
{
// 下面代碼將輸出9。
System.out.println(new InstanceInitTest().a);
}
}
- 直接執行代碼將輸出9,這表明int a = 9這行代碼在初始化塊代碼後執行,但如果將int a = 9放到初始化塊代碼前,程序將輸出6,初始化塊代碼再次將a設爲6
- Java創建一個對象的時候,系統先爲該對象的所有實例變量分配內存(前提是該類已經被加載過了),接着程序開始對這些實例變量執行初始化,順序是先執行初始化塊或聲明實例變量時指定的初始值(取決於源碼中的順序),在執行構造器裏指定的初始值
構造器和初始化塊的作用及區別
初始化塊是構造器的補充,它總是在構造器之前執行,系統同樣可以使用初始化塊來進行對象的初始化操作
與構造器不同的是,初始化塊是一段固定執行的代碼,不接受參數,不能調用,因此初始化塊對同一個類的所有對象所進行的初始化處理完全相同,如果有一段初始化代碼對所有對象完全相同,且無需接受任何參數,便可以將這段代碼提取到初始化塊中
- 實際上初始化塊是個假象,使用javac編譯Java類後,該Java類中的初始化塊會消失,被還原到每個構造器中,且位於構造器所有代碼的前面。
- 與構造器類似,創建一個Java對象時,不僅會執行該類的普通初始化塊和構造器,而且系統會一直追溯到java.lang.Object類,先執行java.lang.Object類的初始化塊,然後執行java.lang.Object的構造器,依次向下執行其父類的初始化塊,然後執行其父類的構造器…最後才執行該類的初始化塊和構造器並返回該類的對象。
- 如果希望類加載後,對整個類進行某些初始化操作,則需要使用static關鍵字來修飾,即使用靜態初始化塊來初始化類
靜態初始化塊
- 初始化塊代碼如果使用了修飾符static,則被稱爲靜態初始化塊,或者類初始化塊(普通初始化塊負責對對象執行初始化,類初始化塊負責對類進行初始化)
- 靜態初始化塊是類相關的,用於對整個類進行初始化處理,通常用於對類變量執行初始化處理,系統將在類初始化階段執行靜態初始化塊,而不是在創建對象時才執行,因此靜態初始化塊總是比普通初始化塊先執行
- 靜態初始化塊不能對實例變量進行初始化處理
- 靜態初始化塊也被稱爲類初始化塊,也屬於類的靜態成員,同樣需要遵循靜態成員不能訪問非靜態成員的規則,包括不能訪問實例變量和實例方法
- 與普通初始化塊類似的是,系統在類初始化階段執行靜態初始化塊時,不僅會執行本類的靜態初始化塊,而且會一直追溯到java.lang.Object類(如果它存在靜態初始化塊),先執行java.lang.Object類的靜態初始化塊,然後執行其父類的靜態初始化塊…最後才能執行該類的靜態初始化塊經過這個過程才能完成該類的初始化過程,只有當類初始化完成後才能使用它
class Root
{
static{
System.out.println("Root的類初始化塊");
}
{
System.out.println("Root的實例初始化塊");
}
public Root()
{
System.out.println("Root的無參數的構造器");
}
}
class Mid extends Root
{
static{
System.out.println("Mid的類初始化塊");
}
{
System.out.println("Mid的實例初始化塊");
}
public Mid()
{
System.out.println("Mid的無參數的構造器");
}
public Mid(String msg)
{
// 通過this調用同一類中重載的構造器
this();
System.out.println("Mid的帶參數構造器,其參數值:"
+ msg);
}
}
class Leaf extends Mid
{
static{
System.out.println("Leaf的類初始化塊");
}
{
System.out.println("Leaf的實例初始化塊");
}
public Leaf()
{
// 通過super調用父類中有一個字符串參數的構造器
super("瘋狂Java講義");
System.out.println("執行Leaf的構造器");
}
}
public class Test
{
public static void main(String[] args)
{
new Leaf();
new Leaf();
}
}
- 第一次創建一個Leaf對象時,因爲系統還不存在Leaf類,因此需要先加載並初始化Leaf類,初始化Leaf類的時候會先執行其頂層父類的靜態初始化塊,在執行其直接父類的靜態初始化塊,最後才執行Leaf本身的靜態初始化塊
- Leaf類初始化成功後,它將在虛擬機中一直存在,因此當第二次創建Leaf實例時無須再次對Leaf類進行初始化
- 普通初始化塊和構造器的執行順序與前面介紹的一致,每次創建一個Leaf對象時,都需要先執行最頂層父類的初始化塊、構造器然後執行其父類的普通初始化塊、構造器…最後才執行Leaf類的初始化塊和構造器
靜態初始化塊和聲明靜態成員變量時所指定的初始值都是該類的初始化代碼,他們的執行順序取決於源程序中的先後順序
public class StaticInitTest
{
// 先執行類初始化塊將類變量a賦值爲6
static
{
a = 6;
}
// 再將類變量a賦值爲9
static int a = 9;
public static void main(String[] args)
{
// 下面代碼將輸出9
System.out.println(StaticInitTest.a);
}
}
- 直接執行代碼將輸出9,這表明static int a = 9這行代碼在靜態初始化塊代碼後執行,但如果將static int a = 9放到靜態初始化塊代碼前,程序將輸出6,靜態初始化塊代碼再次將a設爲6
- 當JVM第一次主動使用某個類時,系統會在類準備階段爲該類的所有靜態成員變量分配內存,在初始化階段則負責初始化這些靜態成員變量,初始化靜態成員變量就是執行類初始化塊代碼或者聲明類成員變量時指定的初始值,順序取決於他們在源程序的先後