为什么Java文件中只有一个公共类

65

为什么 Java 文件中只能有一个公共类且该类名必须与 Java 文件名相同?


63
因为他这么说了! - jjnguy
2
为了让编译器更容易找到类定义,最好以这种方式进行编译。我没有参考资料(这就是为什么这是一条注释),但我认为下面的答案指向了这一点。 - OscarRyz
2
换句话说,如果您不同意,请发明自己的编程语言 :) - BalusC
13个回答

37

它强制所有Java代码以特定的方式进行组织,从长远来看有助于提高代码的可读性。

Java设计者选择了一种严格的方法来执行他们所认为的良好设计实践,这是该主题的一部分。与Perl中的任何事情都可以的态度形成鲜明对比。


1
+1,完全同意,天哪,我在C#中想念这个功能。尽管我总是为每个类/枚举/结构使用单独的文件,但我已经看到了一些可怕的事情 :-) - Darin Dimitrov
3
应该更加严格,同时也适用于非公共类。大多数人都做得很好,但有些人仍然会通过将许多默认可见类放在一个文件中而搞砸事情。 - starblue
2
你有支持这种限制是由于代码可读性的设计师选择它的引用吗? - Rohit
@starblue 不要混淆私有类和内部类。内部类(非静态的)允许直接访问父对象,并且所有实例必须使用 object.Class 而不是仅使用 Class 实例化。相比之下,它们在功能上非常不同。 - Qix - MONICA WAS MISTREATED
@Qix: 如果一个类的存在仅仅是用于保存另一个类的方法返回的数据,但是该类的实例没有理由持有创建它们的对象的引用,如果不是在创建它的文件中声明,那么最合适的地方应该在哪里? - supercat
显示剩余4条评论

19
根据这个来源,它是为了编译效率而设置的:

在侧边栏中解释道:“虽然对于高效的软件包导入是必要的,但编译器尚未强制执行此限制。”

很明显-像大多数设计原因一样-编译器必须通过所有编译单元(.java文件)进行额外的遍历,以确定类的所在位置,这会使编译变得更慢。

在IDE中导入源文件也是同样的情况。另一个原因是源文件的大小要合理。

9

以下是规则。虽然并不完全正确。你可以像这样在"main"类中定义内部类:

public class A {  
   public class B {  
       ...  
   }  
}

2
非公共顶级类同样有效。 - user85421

3
为了理解这些限制背后的基本原因,让我们假设编译器不会为文件名与公共类名不同而产生编译错误。
假设有一个名为A的包。
        A
      /   \
file1.java   file2.java

file1.java

package A;

class file1
{
  public static void main(String args[])
  {

  }
}

public class file3
{
 public static void main(String args[])
 {

 }
}

现在我们知道一个公共类也可以在包外部访问,现在开发人员的责任就是使其对外可访问。让我们看看如何实现:
假设包A仅包含Java文件(没有类文件),而某个包外的类尝试访问public class file3,编译器将首先尝试查找file3.class(不可用),然后尝试查找file3.java(不可用)。因此,即使file3类是公共的,但它对外部世界不可见。因此,如果编译器施加限制,即如果一个文件包含公共类,则应将其命名为公共类名称,那么以上问题就可以解决,开发人员无需考虑将公共类暴露给外部世界。
编译器还施加了限制,即每个Java文件最多只能有一个public类,以便外部世界可以访问每个public类。

3
在任何Java编译单元(.java源文件)中,我们只能有一个顶级公共类或接口。但是每个src文件可以有任意数量的默认类/接口。
为什么呢?JLS把这个选项交给了Java编译器,大多数编译器实现会强制要求文件名与: 1. 公共类/接口名称相同; 2. 如果存在主方法且没有公共类,则为任何名称; 3. 如果存在主方法和公共类,则主方法应在该公共类中; 4. 如果没有公共类和主方法,则为任何有效名称,可能与文件中的类/接口名称匹配,也可能不匹配。 从(2):如果允许两个公共类,则必须给文件两个名称,这对于文件系统来说是非常无意义的。 从(3):如果允许两个公共类,则必须有两个主方法,这对于Java来说是非常无意义的。 因此,Java源文件只能有一个公共类。
我认为编译器强制执行上述4点,使编译器和JVM快速容易地找到特定的Java源文件或类文件。Java具有这样的内置限制,开发人员应遵守以获得更好的编程。
来源:我的读物和理解。

3
感谢Dr Heinz Kabutz和他出色的newsletter提供的帮助。 为什么每个公共类都要放在单独的文件中? 这是我在课程中经常被问到的问题。直到现在,我还没有一个好的答案来回答这个问题。在第一节中,我们读到:“尽管每个Oak编译单元可以包含多个类或接口,但每个编译单元最多只能有一个公共类或接口。”
在侧边栏中解释了原因:“尽管编译器尚未强制执行此限制,但对于有效的包导入是必要的。”
很明显 - 就像大多数事情一样,一旦你知道设计原因就会很明显 - 编译器必须通过所有编译单元(.java文件)进行额外的遍历,以确定哪些类在哪里,这将使编译变得更慢。

1
为了使编译器和程序员之间有一个理解,有一个规则是源代码必须最多只有一个公共类,并且该类必须包含主函数。因此,编译器可以访问(public)类并将类名命名为类文件,而不会产生混淆/限制。另外,由于这个类包含main()函数,执行类文件将得到正确的流程。

1

Java利用这种约定通过从类路径开始扫描子目录中的包层次结构来查找类/接口字节码。该层次结构的文件系统表示还强制执行一些基本规则。

  1. 在同一个包中的任何两个Java类或接口不能具有相同的名称。文件名会冲突。
  2. 在同一个父包中的任何两个Java包都不能具有相同的名称。文件夹路径会冲突。
  3. 类可以访问同一包中的所有类,无需修改类路径。

1
公共修饰符与类一起使用时,对外公开,这意味着包外的任何其他类也可以访问它以解决依赖关系。 现在,包含多个类的此依赖链被验证两次 - 一次是在编译时(验证了一个级别的依赖关系),另一次是在执行时(验证了整个依赖链)。
现在假设有一个公共类(类A),其源文件名与类名不同。现在,如果某个其他类(比如类B)依赖于它,则在编译类B时,Java编译器肯定会检查类A位于何处,并且为此编译器必须浏览所有包以搜索类A,这需要时间。
现在假设在相同的情况下,我们将类A的源文件名命名为A.java,那么在编译类B时,编译器将搜索类A,并且只需要找到名称为A.java的源文件即可,因此它不需要浏览所有包(可能包含多个类),它现在只搜索存在源文件A.java的那个包。
因此,从编译时间和性能的角度来看,一个源文件应该只有一个公共类。

0

我认为这可能是一个可能的原因。 在Java文件中只能有一个公共类,因为Java文件的名称与公共类的名称相同。显然,我们不能拥有两个不同名称的文件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接