Effective Java笔记(25)限制源文件为单个顶级类

        虽然 Java 编译器允许在一个源文件中定义多个顶级类,但这么做并没有什么好处,只会带来巨大的风险 。 因为在一个源文件中定义多个顶级类,可能导致给一个类提供多个定义 。 哪一个定义会被用到,取决于源文件被传给编译器的顺序 。

        为了更具体地说明,下面举个例子,这个源文件中只包含一个 Main 类,它将引用另外两个顶级类( Utensil 和 Dessert )的成员:

public class Main {
    public static void main(String[] args) {
        System.out.println(Utensil.NAME + Dessert.NAME);
    }
}

        现在假设你在一个名为 Utensil.java 的源文件中同时定义了 Utensil 和 Dessert :

class Utensil {
    static final String NAME = " pan";
}
class Dessert {
    static final String NAME = "cake";
}

当然,主程序会打印出“ pancake ” 。

现在假设你不小心在另 一 个名为 Dessert. java 的源文件中也定义了同样的两个类 :

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 这两个定义 。

        如果用命令 javac Main.java 或者 javac Main.java Utensil.java 编译程序,结果将如同你还没有编写 Dessert.java 文件一样,输出 pancake 。 但如果是用命令 javac Dessert.java Main java 编译程序,就会输出 potpie 。 程序的行为受源文件被传给编译器的顺序影响,这显然是让人无法接受的 。

        这个问题的修正方法很简单,只要把顶级类(在本例中是指 Utensil 和 Dessert )分别放入独立的源文件即可 。 如果一定要把多个顶级类放进一个源文件中,就要考虑使用静态成员类,以此代替将这两个类分到独立源文件中去 。 如果这些类服从于另一个类,那么将它们做成静态成员类通常比较好,因为这样增强了代码的可读性,如果将这些类声明为私有的,还可以使它们减少被读取的概率 。 以下就是做成静态成员类的范例:

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";
    }
}

        结论显而易见 : 永远不要把多个顶级类或者接口放在一个源文件中 。 遵循这个规则可以确保编译时一个类不会有多个定义 。 这么做反过来也能确保编译产生的类文件,以及程序结果的行为,都不会受到源文件被传给编译器时的顺序的影响 。

猜你喜欢

转载自blog.csdn.net/java_faep/article/details/132096308
今日推荐