为什么Java允许我们编译一个类名与文件名不同的类?

172

我有一个名为Test.java的文件,并且里面有以下代码。

public class Abcd
{
        //some code here

}

现在这个类无法编译,但是当我移除public修饰符时,它可以成功编译。

为什么Java允许我们编译一个类名与文件名不同的类(当它不是public类)?

我知道这是一个新手问题,但我找不到一个好的解释。


28
因为Java。(因为它不是公开的,所以不必遵循相同的命名规范。除此之外,你需要问发明它的人。) - Dave Newton
2
我怀疑是否有一个“好的解释”。这是公共类的要求,但对于非公共类被认为是不必要的。 - Kayaman
2
看起来是一个类似的问题: https://dev59.com/b1vUa4cB1Zd3GeqPxdu_ - sanket
4
为什么这个问题会有那么多点赞,首先它是一个重复的问题:https://dev59.com/b1vUa4cB1Zd3GeqPxdu_ - G M Ramesh
2
@ Ramesh:这个问题的标题和内容比其他类似的问题更好。 - Jayan
显示剩余2条评论
8个回答

326

这个规则的理由是允许一个.java文件中有多个顶层类。

许多类(例如事件监听器)仅在本地使用,早期版本的Java不支持嵌套类。如果没有这个“文件名=类名”的规则放宽,每个这样的类都需要自己的文件,结果会不可避免地导致无休止的小.java文件的大量增加,以及紧密耦合代码的散布。

一旦Java引入了嵌套类,这个规则的重要性显著下降。今天你可以浏览许多Java文件,从未碰到过一个利用它的。


60
+1,这实际上提供了一个“原因”,也就是问题。 - Dave Newton
4
特别感谢历史信息的提供,我猜想随着嵌套/匿名类的出现,如果现在做出相同的决定(不考虑向后兼容性),让每个文件只允许一个顶层类可能更有意义。 - Michael Berry
3
否认其他人更喜欢使用文本编辑器和命令行工具进行开发,因为你喜欢的 IDE 存在,就像说创建 IDE 没有意义,因为你可以不用它们进行开发一样愚蠢。优秀的开发人员使用这两种方法来创建高质量的代码;所有开发人员都达成一致并唱起“康巴拉”歌曲的可能性很小,而我们所有人都将同意单一最佳编程语言的可能性更小。 - Dan Is Fiddling By Firelight
1
你的意思是使用文本编辑器而不是专门为Java开发设计的IDE(其中包括你的文本编辑器)有优势吗?你是否认为文本搜索比基于IDE的类声明(数据库)查找更具优势?什么是“你偏爱的IDE”?说你的纯文本编辑器完全不亚于一个完整的java IDE,这是一个强有力的论点吗?我理解“优秀的开发者”使用低级编程时没有选择。然而,IDE在所有参数上都优于纯文本编辑器,这是一个简单事实。这不是“我的偏好”。 - Val
5
使用Emacs(或Vim,随意选择)和Unix Shell工具的组合也许不像现代IDE那样方便灵活,学习难度也更大,但与我曾经尝试过的所有IDE相比,它们有两个绝对优势:无论代码库有多大,它们永远不会崩溃,并且可以跟上我的打字速度。 - zwol
显示剩余14条评论

81

原因与门牌相同。如果有人正式居住在办公室(声明为公共场所),他/她的姓名必须出现在门牌上,比如“Alex Jones”或“Colombo侦探”。如果有人只是访问房间、与官员交谈或清洁地板,他们的名字就不需要正式放在门上。相反,门上可以写着“设施”或“会议室”等字样。

Official name or MyClass.java Meeting room or Test.java


4
有趣的类比,加上一点解释如何直接相关可能会更好。原帖作者可能难以建立联系(尽管我理解得很清楚)。 - Andrew Barber
4
@AndrewBarber,我认为这个比喻并不太合适,因为它没有对应到一个公共类与多个包级私有类之间的情况。这就像门牌上写着“Heather Santee, Manager”,但实际上房间里除了Heather还有她的两个秘书。 - Marko Topolnik
@MarkoTopolnik 我不应该卷入这个问题;我在类比方面非常糟糕!;) - Andrew Barber
@AndrewBarber 我本来就想写它,你只是给了我动力 :) 这个比喻也未能表达最严重的问题:正因为这个特性,编译器必须解析所有文件才能发现所有类,否则它可以只读取目录列表并知道所有顶级类的名称。 - Marko Topolnik
@AndrewBarber,这个比喻非常适合目录的概念,在长廊中你可以通过看门牌轻松找到一个人,而不需要进入每个房间并问询。 - exebook

30

Java规范规定一个文件中只能有最多一个公共类,此时类名应与文件名相匹配。所有非公共类都可以拥有任何名称,无论文件名如何。


20
但是,“Java为什么允许”我们这样做的推理是什么? - Marko Topolnik
8
但是,不阻止我们的背后有什么理由? - Marko Topolnik
2
我的看法是:也许它是为了更快地定位类路径内的类而设计的。采用这种约定,检查文件名/路径就足以发现类。如果没有这个约定,类路径类加载器可能需要打开和解析文件才能找到类。 - Andrei Nicusan
@isnot2bad,您是在说我们不允许在它们自己的文件中声明包私有类吗? - Marko Topolnik
你可以这样做,但不必按照文件名来命名它们。 - Andrei Nicusan
显示剩余8条评论

14

我认为,允许匿名类是嵌套类的前提条件。特别是匿名类,它极大地减少了所需的 .java 文件数。如果没有这种支持,您需要将许多单一方法接口实现放在它们自己的单独文件中,从主要使用它们的类中分离出来。(我特别考虑了 action listeners)

在 Oracle 网站的嵌套类 Java 教程中有对所有嵌套类的很好解释,其中包括每个示例。它还有一个它们有用的原因,我会引用:

为什么使用嵌套类?

使用嵌套类的强制性原因包括以下内容:

  • 逻辑上分组只使用一个位置的类的方式: 如果一个类只对另一个类有用,那么将其嵌入该类并将两者保持在一起是合理的。嵌套这样的“辅助类”可以使其包更加简化。

  • 增加封装性: 考虑两个顶层类 A 和 B,其中 B 需要访问 A 的成员,否则将声明为私有。通过将类 B 隐藏在类 A 中,A 的成员可以被声明为私有,并且 B 可以访问它们。此外,B 本身可以对外界隐藏。

  • 它可以导致更易读和易维护的代码: 将小类嵌套在顶级类中,将代码放置在使用它的地方附近。

(我强调的)

我不熟悉早期的 Java 规范,但快速搜索显示内部类是在 Java 1.1 中添加的。


在一个类型仅在另一个类型内有用,但前者的实例与后者的实例无关联的情况下,应该怎么做? - supercat
嵌套类是Java 1.2实现Lambda或“一切皆为对象的一级函数”的方式。这在1.8语法中正在改变。当我们想要在Java类型系统中建模代数数据类型时,也会使用它们。 - hawkeye

12

我持相反观点。程序员应该独立选择类名和文件名才是自然的状态。可能出于简化编译时从包外查找公共类的目的,有一个特殊限制,即公共类必须在与其对应的文件中。


4
请注意,Java区分大小写,但文件系统可以不区分大小写。如果文件的基本名称为"abcd",但类名为"Abcd",那么在不区分大小写的文件系统上是否符合规则?当转移到区分大小写的文件系统时,显然是不符合规则的。
或者假设您有一个名为ABCD和一个名为Abcd的类(让我们不谈这是个坏主意:这可能会发生),并且该程序被移植到不区分大小写的文件系统中。现在你不仅要重命名文件,还要重命名类,糟糕!
又或者如果没有文件呢?假设您有一个可以从标准输入接收输入的Java编译器。那么类必须命名为“StandardInput”吗?
如果您理性地探索要求文件名遵循类名的含义,您会发现这种做法在多个方面都是不好的想法。

我同意你的观点,但我不知道它是否特别回答了问题,除非允许非公共类名与文件名不同,否则可能会缓解由命名架构引起的一些问题。顺便说一句,关于大小写敏感性,如果我正在派生一种语言,在任何范围内声明了Foo,标识符FOOfoofOo等都将是“未定义”的,即使它们存在于外部范围内。这样的设计将消除文件名的大小写敏感问题。 - supercat

3
另外,很多答案没有指出的一点是,如果没有公共声明,JVM就不知道需要调用哪个类的主方法。在一个.java文件中声明的所有类都可以有main方法,但只有标记为public的类上运行主方法。希望对您有帮助。

这是不正确的。你可以有一个名为two.java的文件,其内容如下: class one { public static void main(String[] args){ System.out.println("Hello!"); System.out.println("World!"); } } 现在,当你编译它"javac two.java"时,你会得到一个类文件"one.class",然后当你运行"java one"时,它会正确输出: Hello! World! 它能运行是因为我们说"java one",JVM知道要寻找一个名为"one.class"的类文件,在其中找到main方法,并运行它。你的第二行也是不正确的。 - Kushal Kumar

0
因为一个Java文件可以包含多个类,所以一个Java文件中可能有两个类。但是如果一个Java文件包含一个公共类,那么这个Java文件必须要有一个与文件名相同的类名。

不,那个规则只适用于公共类。 - deadboy

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