2010-04-09 175 views
11

回到這不是有效的代碼:靜態初始化

public class MyClass 
{ 
    private static boolean yesNo = false; 

    static 
    { 
     if (yesNo) 
     { 
      System.out.println("Yes"); 
      return; // The return statement is the problem 
     } 
     System.exit(0); 
    } 
} 

這是一個愚蠢的例子,但在靜態類的構造函數,我們不能return;。 爲什麼?這是否有很好的理由?有人對此有更多瞭解嗎?

因此,我應該做return的原因是在那裏結束施工。

感謝

+4

這些被稱爲靜態構造函數,而不是靜態構造函數。只是寫這個來幫助搜索。 – Oak 2010-04-09 12:06:46

+0

@Oak:謝謝(15個字符) – 2010-04-09 12:18:41

回答

15

我認爲原因是初始化器與字段初始化一起攜帶(並且在實例初始化器的情況下帶有構造函數)。換句話說,JVM只識別一個地方來初始化靜態字段,因此所有初始化 - 無論是否在塊中 - 都必須在那裏完成。

因此,舉例來說,當你寫一個類:

class A { 
    static int x = 3; 
    static { 
     y = x * x; 
    } 
    static int z = x * x; 
} 

然後,它實際上是因爲如果你寫的:

class A { 
    static int x, y, z; 
    static { 
     x = 3; 
     y = x * x; 
     z = x * x; 
    } 
} 

,如果你看一下拆卸這證實:

static {}; 
    Code: 
    0: iconst_3 
    1: putstatic  #5; //Field x:I 
    4: getstatic  #5; //Field x:I 
    7: getstatic  #5; //Field x:I 
    10: imul 
    11: putstatic  #3; //Field y:I 
    14: getstatic  #5; //Field x:I 
    17: getstatic  #5; //Field x:I 
    20: imul 
    21: putstatic  #6; //Field z:I 
    24: return 

所以,如果你會在你的靜態初始化器的某個地方添加一個「返回」,它也會有e防止z被計算。

+1

當我看到這些時,我正要寫這些。還要注意,你可以在一個類中有多個靜態初始化塊。 – 2010-04-09 12:23:26

+0

真的很好的答案,橡樹。它還說明了爲什麼在靜態初始化器中突然完成被認爲是非常糟糕的(正如Joe在評論中指出的那樣)給出了編譯時錯誤。 – jalopaba 2010-04-09 12:49:01

2

你應該返回?在靜態初始化器中沒有調用者,所以根據我的看法,返回沒有任何意義。靜態初始化器在類首次加載時執行。

10
  • 程序流程總是可以結構化而不需要return。 (在你的榜樣將System.exit(0)else子句將達到預期的效果)

  • 在你真正需要它,你可以在一個靜態方法移動代碼,並從初始調用它:

static { 
    staticInit(); 
} 

private static void staticInit() { 
    if (yesNo) { 
     System.out.println("Yes"); 
     return; 
    } 
    System.exit(0); 
} 

請注意,這不是一個靜態構造函數,這是一個靜態初始化。沒有任何建設。

+0

我知道你可以用簡單的'else'來解決它。但我說這是一個非常愚蠢的例子。但它是真的! – 2010-04-09 12:05:10

+0

@Martijn Courteaux是的,我明白這一點。這就是爲什麼它在括號內,只是我的泛化的一個例子。 – Bozho 2010-04-09 12:08:00

1

JSL regarding static initializers

「這是一個靜態初始化一個編譯時錯誤能夠與檢查異常(§11.2)突然完成(§14.1,§15.6)這是一個編譯期。如果靜態初始化器無法正常完成(§14.21),則會出現時間錯誤。「

Abrupt completion(其中包括):「迴歸沒有任何價值」,「與給定值返回」等

所以靜態初始化return語句是「突然完成」,併產生一個編譯期時間錯誤。

+1

JSL沒有指定一個理由,但這是這個問題的關鍵。 – Oak 2010-04-09 12:11:16

+1

我剛剛發佈了相同的東西,但我試圖想:爲什麼「突然完成」一個靜態初始化器如此糟糕,以致於它使它成爲編譯錯誤? – 2010-04-09 12:11:51

+0

@Oak:你說得對,沒理由。規格有時像公理。 – jalopaba 2010-04-09 12:20:58

0

我知道靜態初始化器的規則是每加載一次類的字節代碼之後,在執行任何靜態方法或實例化類中的第一個對象之前,它們都只執行一次。 JLS保證這個初始化已經完成。爲了確保這一保證是真實的,JLS還規定代碼不能突然終止(在另一個答案中明確給出)。

請注意,可以在不初始化的情況下加載字節碼;見Class.forName(String, boolean, ClassLoader)方法。如果boolean參數是false那麼這將加載類但不初始化它。程序員仍然可以做一些反思來發現關於這個類的信息,而不用初始化它。但是,一旦您嘗試通過調用靜態方法或實例化實例來直接使用該類,那麼JVM將繼續首先初始化它。

如果任何靜態初始化程序會突然終止 - 這可能發生在RuntimeException上,則該類將保持無效狀態。第一次,JVM會拋出一個ExceptionInInitializeError(注意這是一個Error,這意味着它被認爲是內部故障)。從這一點開始,將不可能使用該類 - 嘗試調用靜態方法或實例化一個對象,您將得到一個NoClassDefFoundError

從這種情況下,無需重新啓動JVM是,如果你使用的是ClassLoader S和可替代的類加載器與故障類和重建類或重新初始化器在不同的環境中恢復(也許是不同的系統屬性)的唯一方法,但該計劃必須爲這種情況做好充分的準備。

0

我會重新排列語句,使其更簡單/更短。如果if/else的兩個分支都需要返回,將永遠不會有好的情況。

static { 
    if (!yesNo) 
     System.exit(0); // silently exiting a program is a bad idea!" 
    System.out.println("Yes"); 
}