为什么Java中的文件名与公共类名相同?

63
在Java中,文件的名称应该与包含在文件中的public class的名称相同。这个限制是为什么?它有什么用途?

32
这个问题应该重新表述一下:为什么任何一种语言都会故意让查找源代码变得更加困难,而没有任何好处,只是允许任意的文件/类命名不匹配? - Bill K
相关链接:https://dev59.com/gmYr5IYBdhLWcg3wRoOW - starball
7个回答

50

Java采用了一种有趣的方法:当给程序员多个选项只会削弱编程体验时,就移除这些选项。

他们在许多地方都这样做了。文件名和包名肯定是如此,但也不允许在一个文件中拥有多个公共类(从来不好),也不允许将类分割成多个文件(非常难以处理!)等。

语言在很多决策上都会影响可用性和可维护性。还有一些可能是不错的选择:

公共变量:我从来没有需要过它,并且我从来没有见过一种情况,其中一些聪明的程序员认为需要其中之一并且实际上是正确的。

方法/类大小限制确实不错,但这可能有些棘手(可以通过代码检查器轻松实施,问题通常在于最需要帮助的公司却不知道自己需要帮助,因此不使用代码检查器等工具)。

回答的重点是,尽管这些对于大多数小团队并不重要,但当您的团队扩大并且拥有多个分布在世界各地的分立团队和顾问时,您真的会感到这种不灵活性的重要性。


对于设置器和获取器的评论回复:

Java bean是Borland创建的一个丑恶物来混合他们的GUI,然后被改装成Java。

这是个可怕的想法--它分散了OO编程的注意力--Getter和setter A)显示了太多的实现细节,并且B)让您以操作其他对象的数据而不是请求其他对象为您执行操作的方式思考。这是为那些还不能以OO思考的人准备的坏hack。

偶尔需要使用Getter,但除非绝对避免不了,否则不应添加。

应尽可能避免使用Setter。如果您绝对需要在对象构造后外部修改状态,请尝试使用构建器模式并保护您的Setter在任何操作被执行后不被调用。

显然,所有事情都有例外,许多“getter”实际上是关键的对象业务逻辑,例如String.length(),无论如何实现String都需要该方法,并且甚至不能通过返回属性来实现 - 如果你愿意称其为“getter”的话,这是一个很好的例子。


3
在Java设计时,还没有确立getX/setX编程模式。我认为这是因为JavaBeans必须编写接口代码,而在接口中无法有公共变量。 - Thorbjørn Ravn Andersen
公共变量在类实际上是一个被赞美的结构体并且只有公共(无意义的)getter和setter时非常有用。 - Richard Tingle
虽然在实践中你是完全正确的,但偶尔使用它确实很有用,但从理论上讲,这将是可怕的面向对象编程,不应该这样做。我以前也用过这种方法,但只是作为一种hack,并且通常最终会将其删除。所以实际上,你可以只有公共变量这一事实只会损害你的代码,我想我坚持认为它们不应该存在。只有setter和getter的类或只有公共变量的类是一个非常糟糕的模式。 - Bill K
是的,即使我被迫使用某种数据对象(例如,你正在处理一个晦涩的JSON解析器,并且它想要将所有内容转换为对象),我仍然会避免使用setter,而是使用构建器或通过构造函数传递所有内容。 - Hakanai
@BillK 当你明确提到印度和中国顾问作为赞赏不灵活性的原因时,你到底意味着什么?这可能是我在SO上读过的最奇怪的事情。 - Chetan Kinger
显示剩余5条评论

19

我本来想说这是绝对必须的。但是我看了一下JLS,发现它并不那么严格。从JLS的角度来看,设置此类限制是由编译器自行决定的。

实际上,通常使用的编译器确实有这种限制,正如其他人已经解释的那样,对于编译器查找编译单元或者类加载器查找类文件来说,设置这种限制会更容易些。


8
就语言规范而言,源代码甚至不需要包含在文件中。这只是编译器处理源代码时的惯例。为了方便处理,才把源代码放在文件中。 - Tom Hawtin - tackline

12

更具体地说,文件名应该与该文件中的公共类名称相同,这是告诉JVM这是您的入口点的方法。


实际上,入口点是在jar清单中指定的。它与Java级别的源文件排列无关。 - Antimony
@Antimony,如果你需要运行单个Java文件,那么入口点在清单中提到的命名约定应该相同。 - GuruKulki
1
这与入口点无关。您可以在名为Bar.java的文件中创建一个非“public”类,称为Foo,编译它,并通过java Foo运行它。 java工具不关心类是否为“public”,并通过您给定的类名确定入口点,而不是源文件的名称(如果关闭调试信息,则源文件甚至不在类字节码中)。其他启动项(运行jar、servlet等)同样以其他方式确定入口点,而不是源文件名。 - T.J. Crowder

5
这只是由Java的制造商Sun设定的规范。目的是为了组织,原因是让所有编写Java代码的人都有一种一致的命名文件的方式。

我对这个答案进行了负评,因为它是错误的。我的编辑只是纠正了这个答案。你决定回滚它。现在这是你的事了。 - nbro
@nbro:这个答案是100%正确的。你的编辑只是用不同的措辞说了同样的话,同时删除了有用的链接(该链接已经失效但现在已修复)。phisch的评论也是多余的,因为他正在争论我已经提出的同一观点。 - BlueRaja - Danny Pflughoeft
@BlueRaja-DannyPflughoeft 这个答案并不完全正确,因此是错误的。"原因是为了让所有编写Java代码的人都有一种一致的文件命名方式。"这不是唯一的原因,因此您的答案是不正确的,或者用友好的说法,是误导和不完整的。 - nbro

2
每个公共类必须在一个文件中,其中文件名与类名匹配,并且在一个包中,该包表示目录结构,采用点形式书写(斜杠变成点,例如com/example/app变成com.example.app)。
这种约定不是随意的。编译器必须能够找到源文件,类加载器必须能够找到实现。匹配包名称和类名使得这一过程非常简单,更重要的是快速。
这种约定不适用于非公共类。这是因为非公共类的可见性非常有限,只能在定义它们的包内使用。因此,在两种情况下,编译器和运行时环境已经找到了正确的文件。

非公共的、默认访问(“包私有”)类可以在同一包中的任何地方使用。 - Tom Hawtin - tackline
是的,那实际上是个打字错误。它们可以在包内使用,但不能在其他地方使用。私有类只能在定义它们的文件中使用。 - phisch

1

这对于定位类非常有用。例如,假设允许不同的文件名,如果您已经创建了一个类的实例,则编译器必须在所有文件中搜索该类,而如果文件名与类名相同,则查找和使用类的性能会提高。可能还有其他原因。


-1
只要它不是公共的,一个类就可以有一个与文件名不同的名称。该类还可以具有主方法。类文件将生成与类名相同而不是源文件名的文件。应使用类名来执行它。
原因是:默认类是包私有的,因此javac不必找到这个源文件,以便从包外部编译其他Java程序。

不正确。还有其他限制,请参见JLS 7.6。 - Stephen C

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