Tips
《Effective Java, Third Edition》一書英文版已經出版,這本書的第二版想必不少人都讀過,號稱Java四大名著之一,不過第二版2009年出版,到如今已經將近8年的時間,但隨着Java 6,7,8,甚至9的發佈,Java語言發生了深入的變化。
在這裏第一時間翻譯成中文版。供你們學習分享之用。java
雖然Java編譯器容許在單個源文件中定義多個頂級類,但這樣作沒有任何好處,而且存在重大風險。 風險源於在源文件中定義多個頂級類使得爲類提供多個定義成爲可能。 使用哪一個定義會受到源文件傳遞給編譯器的順序的影響。學習
爲了具體說明,請考慮下面源文件,其中只包含一個引用其餘兩個頂級類(Utensil
和Dessert
類)的成員的Main
類:this
public class Main { public static void main(String[] args) { System.out.println(Utensil.NAME + [Dessert.NAME](http://Dessert.NAME)); } }
如今假設在Utensil.java
的源文件中同時定義了Utensil
和Dessert
:命令行
// Two classes defined in one file. Don't ever do this! class Utensil { static final String NAME = "pan"; } class Dessert { static final String NAME = "cake"; }
固然,main方法會打印pancake
。翻譯
如今假設你不當心建立了另外一個名爲Dessert.java
的源文件,它定義了相同的兩個類:code
// Two classes defined in one file. Don't ever do this! class Utensil { static final String NAME = "pot"; } class Dessert { static final String NAME = "pie"; }
若是你足夠幸運,使用命令javac Main.java Dessert.java
編譯程序,編譯將失敗,編譯器會告訴你,你已經屢次定義了類Utensil
和Dessert
。 這是由於編譯器首先編譯Main.java
,當它看到對Utensil
的引用(它在Dessert
的引用以前)時,它將在Utensil.java
中查找這個類並找到Utensil
和Dessert
。 當編譯器在命令行上遇到Dessert.java
時,它也將拉入該文件,致使它遇到Utensil
和Dessert
的定義。blog
若是使用命令javac Main.java
或javac Main.java Utensil.java
編譯程序,它的行爲與在編寫Dessert.java
文件(即打印pancake
)以前的行爲相同。 可是,若是使用命令javac Dessert.java Main.java
編譯程序,它將打印potpie
。 程序的行爲所以受到源文件傳遞給編譯器的順序的影響,這顯然是不可接受的。接口
解決這個問題很簡單,將頂層類(如咱們的例子中的Utensil
和Dessert
)分割成單獨的源文件。 若是試圖將多個頂級類放入單個源文件中,請考慮使用靜態成員類(條目 24)做爲將類拆分爲單獨的源文件的替代方法。 若是這些類從屬於另外一個類,那麼將它們變成靜態成員類一般是更好的選擇,由於它提升了可讀性,而且能夠經過聲明它們爲私有(條目 15)來減小類的可訪問性。下面是咱們的例子看起來如何使用靜態成員類:ip
// Static member classes instead of multiple top-level classes public class Test { public static void main(String[] args) { System.out.println(Utensil.NAME + [Dessert.NAME](http://Dessert.NAME)); } private static class Utensil { static final String NAME = "pan"; } private static class Dessert { static final String NAME = "cake"; } }
這個教訓很清楚:永遠不要將多個頂級類或接口放在一個源文件中。 遵循這個規則保證在編譯時不能有多個定義。 這又保證了編譯生成的類文件以及生成的程序的行爲與源文件傳遞給編譯器的順序無關。編譯器