为什么 java 个源文件需要包声明?

Why do java source files require package declarations?

我想我没能理解 java 包结构,java 文件在其中有一个包声明,然后也需要出现在一个目录中,这对我来说似乎是多余的匹配包名。例如,如果我有一个 MyClass.java 文件:

package com.example;

public class MyClass {
    public static void main(String[] args) {
        System.out.println("Hello, World");
    }
}

然后我需要将这个文件放在 com/example 中,相对于基本目录,然后我会从基本目录执行 java com.example.MyClass 到 运行 它。

为什么编译器不能通过查看目录结构来推断包名?例如,如果我从基本目录 javac com\example\MyClass.java 编译文件,我不明白为什么 MyClass.java 不隐式属于 com.example 包。

我知道有一个默认包,但源文件中的包声明似乎仍然是多余的信息?

把问题抛到脑后:

假设 package 语句是重要的 - 它代表 class 的命名空间并且属于 class 文件。

所以现在的问题是 - 为什么 classes 必须位于与其包相匹配的文件夹中?

答案是它使查找它们变得容易得多 - 这只是组织它们的好方法。

有帮助吗?

正如您(隐含地)承认的那样,在默认包的情况下,您不需要声明包的名称。让我们把这个狡辩放在一边......

这种看似冗余的原因是,如果没有 package 声明,Java1 源代码的含义将是模棱两可的。例如,路径名为“/home/steve/project/src/com/example/Main.java”的源文件可能有 7 个不同的完全限定名称,具体取决于您编译代码的方式。最有可能的是,其中只有一个是“正确”的。但是您无法通过(仅)查看一个源文件来判断哪个 FQN 是正确的。


还需要注意的是,Java语言规范并不要求您根据包来组织源代码树。这是 Java 编译器(大)系列的要求,但可以编写不需要此要求的符合标准的编译器。例如:

  • 源代码可以保存在数据库中。
  • 源代码可以保存在具有随机文件名的文件树中2.

在这种情况下,package 声明不会重复文件路径名,或(必然)任何内容。但是,除非有一些冗余,否则为 class 找到正确的源“文件”对编译器来说代价高昂……对程序员来说也是有问题的。

上述考虑是大多数 Java 工具链依赖文件树结构来定位源代码和编译 classes 的实际原因。


1 - 我的意思是 Java 的假设方言不需要 package 声明。
2 - 编译器需要扫描文件树以找到所有 Java 个文件,并解析它们以找出哪个文件定义了哪个 class。可以,但不是很实用。

您必须记住,包不仅仅表示文件夹结构。文件夹结构是Java匹配包名的约定,就像class名称必须匹配文件名的约定一样。

需要一个包来消除 class 与其他同名 class 的歧义。例如 java.util.Date 不同于 java.sql.Date。 该包还为同一包中的其他 class 提供了 package-private 方法或成员的访问权限。

你必须反过来看。 class 包含有关其自身的所有信息,class 名称和包名称。然后当程序需要它时,class 还没有加载,JVM 通过查看与包名匹配的文件夹结构和文件名与它匹配的 class 知道在哪里寻找它class 姓名。

事实上根本没有这样的义务。

Oracle JDKs javac(我相信大多数其他实现也一样)将愉快地编译您的 HelloWorld class,无论它位于哪个目录以及您在源文件。

当您编译相互引用的多个源文件 时,目录结构就会出现。此时编译器必须能够以某种方式查找它们。但它在源代码中所拥有的只是所引用的 class 的完全限定名称(甚至可能尚未编译)。

在运行时情况类似:当需要加载 class 时,其完全限定名称是起点。现在 class 加载程序的工作是仅根据 FQN 查找 .class 文件(或 ZIP 文件中的条目,或任何其他可以想象的来源),这也是分层文件系统中最简单的事情就是把包名翻译成目录结构。

唯一的区别是在运行时您的 "standalone" class 也必须由 VM 加载,因此需要查找它,因此它应该位于正确的文件夹结构中(因为这就是 bootstrap class 加载程序的工作方式。