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"; } }
这个教训很清楚:永远不要将多个顶级类或接口放在一个源文件中。 遵循这个规则保证在编译时不能有多个定义。 这又保证了编译生成的类文件以及生成的程序的行为与源文件传递给编译器的顺序无关。编译器