雖然Java編譯器讓你在單個源文件中定義多個頂層類,但是這么做沒有任何益處。危險性來自這個事實:在單個源文件中定義多個頂層類,使得為一個類提供多個定義變得可能贪染。使用哪個定義,受傳遞到編譯器的源文件順序的影響催享。
為了使得這具體些杭隙,考慮如下源文件,它僅僅包含了一個Main類因妙,這個類引用另外兩個頂層類(Utensil和Dessert)的成員:
public class Main {
public static void main(String[] args) {
System.out.println(Utensil.NAME + Dessert.NAME);
}
}
現(xiàn)在假設(shè)你在命名為Utensil.java的單個源文件中痰憎,同時定義Utensil和Dessert:
// 同個文件中定義了兩個類。永遠不要這么做!
class Utensil {
static final String NAME = "pan";
}
class Dessert {
static final String NAME = "cake";
}
當(dāng)然攀涵,主程序打印了pancake铣耘。
現(xiàn)在假設(shè)你恰巧在命名為Dessert.java的另外一個源文件中,定義了相同的兩個類:
// 同個文件中定義了兩個類以故。永遠不要這么做!
class Utensil {
static final String NAME = "pot";
}
class Dessert {
static final String NAME = "pie";
}
如果你足夠幸運用命令javac Main.java Dessert.java編譯這個程序蜗细,那么編譯會失敗,而且這個編譯器將會告訴你:你已經(jīng)多次定義了Utensil and Dessert類。確實如此炉媒,因為編譯器將會編譯 Main.java踪区,而且當(dāng)它看見Utensil(它早于Dessert的引用)的引用,它將在Utensil.java中尋找這個類吊骤,然后發(fā)現(xiàn)了Utensil和Dessert缎岗。當(dāng)編譯器遇見了命令行上的Dessert.java,它也將引入這個文件白粉,這造成了它同時遇見了Utensil和Dessert传泊。
如果你使用命令javac Main.java或者javac Main.java Utensil.java編譯這個程序,它表現(xiàn)為你編寫Dessert.java文件之前的行為鸭巴,即打印pancake眷细。但是如果你使用javac Dessert.java Main.java編譯這個程序,它將打印potpie奕扣。因此這個程序的行為受到源文件傳遞到編譯器順序的影響薪鹦,這是明顯不可接受的掌敬。
解決這個問題惯豆,就像分解這個頂層類(在我們例子情形中,Utensil和Dessert)到不同的源文件這么簡單奔害。如果你很想把多個頂層類放置到單個文件中楷兽,考慮使用靜態(tài)成員類(條目24)作為分解類到不同源文件的替代方法。如果類從屬于另外一個類华临,把他們變成靜態(tài)成員類通常是更好的替代方法芯杀,因為它增強了可讀性,而且通過聲明這個類為私有來減少類的可訪問性(條目15)雅潭。以下是使用靜態(tài)成員類我們的例子看上去的樣子:
// 靜態(tài)成員類而不是多個頂層類
public class Test {
public static void main(String[] args) {
System.out.println(Utensil.NAME + Dessert.NAME);
}
private static class Utensil {
static final String NAME = "pan";
}
private static class Dessert {
static final String NAME = "cake";
}
}
這堂課是明顯的:永遠不要把多個頂層類或者接口放到單個源文件中揭厚。遵從這個規(guī)則保證你不會在編譯時單個類有多個定義。這轉(zhuǎn)而保證扶供,編譯產(chǎn)生的類文件和最終程序的行為筛圆,是獨立于源文件傳入編譯器的順序。