2010-10-12 49 views
161

當我根據C++知識使用Java時,我喜歡用以下方式初始化變量。我是否應該在構造函數或構造函數外初始化變量

public class ME { 
    private int i; 

    public ME() { 
     this.i = 100; 
    } 
} 

一段時間後,我改變習慣

public class ME { 
    private int i = 100; 

    public ME() { 
    } 
} 

我碰到別人的源代碼來了,有的是採用1慣例,別人都在用第二次會議。

我可以知道你們都推薦哪種約定,爲什麼?

+2

你應該初始化使用初始化列表在C++中。否則,它是初始化+分配。 – 2012-07-12 09:46:10

+2

這是http://stackoverflow.com/q/1994218/922348的副本。查看接受的答案以獲得更全面的討論 – rimsky 2012-12-14 17:57:18

+0

它們已經默認值...不需要再次初始化它們。 – 2016-09-11 02:26:18

回答

157

我發現第二種風格(聲明+初始化一次性)優越。理由:

  • 它一目瞭然地清楚變量是如何初始化的。通常,當讀取一個程序並遇到一個變量時,你首先會去聲明它(通常在IDE中是自動的)。使用樣式2時,您會立即看到默認值。對於樣式1,您還需要查看構造函數。
  • 如果你有多個構造函數,你不必重複初始化(並且你不能忘記它們)。

當然,如果初始化值是不同的構造不同(甚至在構造函數計算),你都必須做在構造函數中。

+11

爲了解決第二個問題,我通常創建一個包含代碼的私有構造函數,然後在所有其他構造函數的開頭調用'this(privateConstructorArgs)'。 – tmnol 2015-08-11 00:58:28

+0

除了定義和調用所有其他構造函數的私有構造函數外,還可以定義一個實例初始化方法,該實例初始化方法將在每個構造函數之前自動調用。這樣,當你添加一些新的構造函數時,你不必記得調用私有構造函數。 – TheBaj 2016-05-04 15:52:06

+0

@TheBaj,由於私有構造函數的建議是根據調用的構造函數將變量初始化爲不同的值,因此如何從實例初始化程序中告訴調用哪個構造函數以確定要分配哪個值? – 2016-08-04 23:13:53

19

我傾向於使用第二個到避免複雜的構造(或無用的一個),還我真的不認爲這是一個初始化(即使它是一個初始化),而更像是給人一種默認值。

例如,在您的第二個代碼段中,您可以刪除構造函數並獲得更清晰的代碼。

6

我看到的第一種方法唯一的問題是如果你打算添加更多的構造函數。那麼你會重複代碼和可維護性會受到影響。

+14

您可以始終鏈接您的構造函數。 – Bernard 2010-10-12 20:14:55

+0

@Bernard鏈接構造函數+具有繼承性可能非常糟糕。 – 2010-10-12 20:21:09

+5

@Colin Hebert:它可以,但如果設計得好,它也可以是優雅的。 – Bernard 2010-10-12 20:39:38

3

我建議在構造函數中初始化變量。這就是它們存在的原因:確保您的對象正確構建(初始化)。

無論哪種方式都會工作,這是風格問題,但我更喜歡構造函數的成員初始化。

+3

我並不認爲賦予默認值是構建對象的一部分。但正如你所說,這是一個風格問題。 – 2010-10-12 20:18:41

+0

它們具有默認值...不需要初始化它們AGAIN – 2016-09-11 02:15:39

2

我認爲都是正確的編程明智的,

但我認爲你的第一個選項是在面向對象的方法更正確的,因爲在構造函數中創建對象時,它是當變量應該初始化。

我認爲這是「按書」的慣例,但它是開放的討論。

Wikipedia

4

如果在頂部初始化或構造並不在某些情況下,在構造函數初始化太大的差別。但很有意義。

class String 
{ 
    char[] arr/*=char [20]*/; //Here initializing char[] over here will not make sense. 
    String() 
    { 
     this.arr=new char[0]; 
    } 
    String(char[] arr) 
    { 
     this.arr=arr; 
    } 
} 

所以根據情況有時你會在上面,有時在構造函數初始化。

FYI其他選項的初始化不通過構造函數:

class Foo 
{ 
    int i; 
    static int k; 

    //instance initializer block 
    { 
     //run's every time a new object is created 
     i=20; 
    } 

    //static initializer block 
    static{ 
     //run's only one time when the class is loaded 
     k=18; 
    }  
} 
2

這兩個選項可以根據您的情況是正確的。

一個非常簡單的例子是:如果有多個構造所有這些初始化變量相同的方式(INT x = 2時爲它們中的每一個)。在聲明中初始化變量以避免冗餘是有意義的。

這也是情理之中的考慮在這種情況下最終的變量。如果你知道最終變量在聲明中有什麼價值,那麼在構造函數之外初始化它是有意義的。但是,如果您希望類的用戶通過構造函數初始化最終變量,請將初始化延遲至構造函數。

+0

+1作爲最終變量。我喜歡不可思議的課程:-)。 – sleske 2010-10-12 21:35:40

4

不管初始化字段的方式如何,如果可能的話,使用final限定符將確保字段在多線程環境中的值的可見性。

+1

真的嗎?當我們談論實例字段時,我認爲final關鍵字對可見性沒有影響。一個不同的故事將是臨時領域(在一個方法中定義),那麼我會同意你的看法。但是,如果我錯過了一些東西,請舉個例子。 – bvdb 2015-04-29 13:10:47

+0

這是好的,但這與問題完全一致嗎?你的答案的問題是:「我應該如何申報我的領域,爲什麼?」 – nbro 2015-05-08 10:37:42

0

我會說,這取決於默認。例如

public Bar 
{ 
    ArrayList<Foo> foos; 
} 

我會做一個new ArrayList外的構造,如果我總是假設foos不能爲空。如果Bar是一個有效的對象,不關心如果foos爲空或不,我會把它放在構造函數中。

您可能會不同意,並說它是構造函數的工作,使對象處於有效狀態。但是,如果清楚全部構造函數應該做同樣的事情(初始化foos),爲什麼重複該代碼?

14

我有幾乎總是在構造函數中初始化的做法(習慣)有兩個原因,一個我認爲它增加了readablitiy(更乾淨),另外兩個構造函數中的邏輯控制比一行更多。即使最初的實例變量不需要邏輯,如果需要,在構造函數中給它添加更多靈活性以添加邏輯。

關於上面提到的關於多個構造函數的問題,可以通過一個無參數構造函數來初始化所有實例變量,這些實例變量對所有構造函數都是相同的,然後每個構造函數在第一行調用this() 。這解決了你的重複問題。

1

它可以取決於什麼您正在初始化,例如,如果涉及檢查的異常,您不能只使用字段初始化。例如,以下內容:

public class Foo { 
    FileInputStream fis = new FileInputStream("/tmp"); // throws FileNotFoundException 
} 

會導致編譯時錯誤,除非你也有一個構造函數聲明checked異常,或延長超哪些呢,比如:

public Foo() throws FileNotFoundException {}