靜態成員的初始化順序(C#,java)
阿新 • • 發佈:2018-12-26
前幾天去參加了場筆試,裡面考了靜態建構函式,當時沒做出來,現在對靜態成員的初始化做一個總結。
在c#類中的靜態成員有靜態變數、靜態函式和靜態建構函式,而在java中是沒有靜態建構函式的,取而代之的是靜態程式塊。靜態成員一般存放在靜態區,而且是屬於類的,所以我們可以不用例項化物件,直接呼叫靜態函式,比如工具類的方法一般都宣告為靜態函式。c#和java對靜態成員的初始化順序是不一樣的,下面我將分別對他們進行總結。
1.c#中靜態成員的初始化順序
為了更好的說明,我寫了一個測試程式,為了讓變數在初始化時有列印資訊,我用成員函式對他們賦值,程式如下:class A
{
staticint a = setA
int a1 = setA1();//非靜態變數
privatestaticint setA1()
{
Console.WriteLine("父類非靜態變數");
return1;
}
publicstaticint setA()
{
Console.WriteLine("父類靜態變數");
return1;
}
public A()//建構函式
{
Console.WriteLine("父類建構函式");
}
static A()//靜態建構函式
{
Console.WriteLine("父類靜態建構函式");
}
}
class B : A
{
staticint b = setB();//靜態變數
int b1 = setB1();//非靜態變數
privatestaticint setB1()
{
Console.WriteLine("子類非靜態變數");
return1;
}
publicstaticint setB()
{
Console.WriteLine("子類靜態變數");
return1;
}
public B()//建構函式
{
Console.WriteLine("子類建構函式");
}
static B()//靜態建構函式
{
Console.WriteLine("子類靜態建構函式");
}
}
classProgram
{
staticvoidMain(string[] args)
{
Console.WriteLine("第一次呼叫。。。");
B b
Console.WriteLine("第二次呼叫。。。");
b =new B();
}
}
在上面我定義了一個父類A和一個子類B,再讓子類例項化兩次,並打印出初始化資訊,結果如下:
第一次呼叫。。。
子類靜態變數
子類靜態建構函式
子類非靜態變數
父類靜態變數
父類靜態建構函式
父類非靜態變數
父類建構函式
子類建構函式
第二次呼叫。。。
子類非靜態變數
父類非靜態變數
父類建構函式
子類建構函式
從這裡我們可以看到,靜態變數和靜態建構函式只會在類的第一次例項化時進行初始化,第二次就是正常的初始化了。在正常例項化中,初始化的順序是:成員變數 -> 父類例項化 -> 建構函式。如果有靜態型別的話,就會先初始化靜態型別,於是順序就變成了:靜態變數 -> 靜態建構函式 -> 成員變數 -> 父類例項化 -> 建構函式。在父類例項化中,順序也是這樣的。
1.1.一道筆試題
class A
{
publicstaticint X;
static A()
{
X = B.Y +1;
}
}
class B
{
publicstaticint Y = A.X +1;
static B(){}
staticvoidMain()
{
Console.WriteLine("X={0},Y={1}", A.X, B.Y);
}
}
程式會從B類中的Main()開始執行,所以先初始化靜態變數Y,而Y要呼叫A.X,呼叫A類靜態建構函式A(),此時B.Y未初始化預設為0,所以X=1,再回到B的靜態變數初始化中Y就是2了。初始化完成後,進入Main(),列印X=1,Y=2。意外吧!
2.java中靜態成員的初始化順序
同樣的,只不過靜態建構函式用靜態程式塊代替。publicclassMain{
publicstaticvoid main(String[] args){
System.out.println("第一次呼叫。。。");
B b =new B();
System.out.println("第二次呼叫。。。");
b =new B();
}
}
class A {
privatestaticint a = setA();
privateint a1 = setA1();
publicstaticint setA(){
System.out.println("父類靜態變數");
return1;
}
privateint setA1(){
System.out.println("父類非靜態變數");
return0;
}
public A(){
System.out.println("父類建構函式");
}
static{
System.out.println("父類靜態程式塊");
}
}
class B extends A {
privatestaticint b = setB();
privateint b1 = setB1();
privatestaticint setB1(){
System.out.println("子類非靜態變數");
return1;
}
publicstaticint setB(){
System.out.println("子類靜態變數");
return1;
}
public B(){
System.out.println("子類建構函式");
}
static{
System.out.println("子類靜態程式塊");
}
}
執行結果如下:
第一次呼叫。。。
父類靜態變數
父類靜態程式塊
子類靜態變數
子類靜態程式塊
父類非靜態變數
父類建構函式
子類非靜態變數
子類建構函式
第二次呼叫。。。
父類非靜態變數
父類建構函式
子類非靜態變數
子類建構函式
和c#的一樣,只在第一次例項化時呼叫,但是初始化順序卻不一樣。在java中正常例項化順序是:父類例項化 -> 成員變數 -> 建構函式。加入靜態型別後會先出示話所有的靜態型別(包括父類和子類的),然後才是正常的初始化:父類類靜態型別 -> 子類靜態型別 -> 父類正常例項化 -> 成員變數 -> 建構函式。靜態型別的初始化順序都是先變數後程序塊。