美文网首页
第二十五条:限制源文件为单个顶层类【类和接口end】

第二十五条:限制源文件为单个顶层类【类和接口end】

作者: taogan | 来源:发表于2021-02-12 10:20 被阅读0次

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

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

public class Main {
  public static void main(String[] args) {
    System.out.println(Utensil.NAME + 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";
}

当然,主程序会打印:“pancake”。
现在假设你不小心在另一个名为Dessert.java的源文件中也定义了同样的两个类:

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

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

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

// 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);
  }
  private static class Utensil {
    static final String NAME = "pan";
  }
  private static class Dessert {
    static final String NAME = "cake";
  } 
}

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

相关文章

网友评论

      本文标题:第二十五条:限制源文件为单个顶层类【类和接口end】

      本文链接:https://www.haomeiwen.com/subject/uvzhxltx.html