Java 类名区分大小写

2022-08-31 22:37:25

如果在不同的目录中编写两个具有相同不区分大小写名称的公共 Java 类,则这两个类在运行时都不可用。(我在Windows,Mac和Linux上用几个版本的HotSpot JVM对此进行了测试。如果还有其他JVM可以同时使用,我不会感到惊讶。例如,如果我创建了一个名为和一个命名的类,如下所示:aA

// lowercase/src/testcase/a.java
package testcase;
public class a {
    public static String myCase() {
        return "lower";
    }
}

// uppercase/src/testcase/A.java 
package testcase;
public class A {
    public static String myCase() {
        return "upper";
    }
}

包含上述代码的三个eclipse项目可以从我的网站获得

如果尝试,我像这样调用两个类:myCase

System.out.println(A.myCase());
System.out.println(a.myCase());

类型检查器成功了,但是当我运行由正上方的代码生成的类文件时,我得到:

线程“main” java.lang.NoClassDefFoundError 中的异常:testcase/A (错误的名称:testcase/a)

在 Java 中,名称通常区分大小写。某些文件系统(例如Windows)不区分大小写,因此我对上述行为的发生并不感到惊讶,但似乎是错误的。不幸的是,Java规范对于哪些类是可见的,是奇怪的非承诺。Java Language Specification (JLS), Java SE 7 Edition (Section 6.6.1, page 166) 说:

如果将类或接口类型声明为公共,则任何代码都可以访问它,前提是声明它的编译单元 (§7.3) 是可观察的。

在第7.3节中,JLS以极其模糊的术语定义了编译单元的可观察性:

预定义包 java 及其子包 lang 和 io 的所有编译单元始终是可观察的。对于所有其他包,主机系统确定哪些编译单元是可观察的

Java 虚拟机规范同样模糊(第 5.3.1 节):

以下步骤用于加载,从而使用引导类加载程序创建由[二进制名称] N表示的非数组类或接口C [...]否则,Java 虚拟机将参数 N 传递给引导类装入器上某个方法的调用,以以依赖于平台的方式搜索 C 的假定表示形式。

所有这些都导致四个按重要性降序排列的问题:

  1. 有没有关于每个JVM中的缺省类装入器可以装入哪些类的保证?换句话说,我能否实现一个有效但退化的JVM,除了java.lang和 java.io 中的类之外,它不会加载任何类?
  2. 如果有任何保证,上面示例中的行为是否违反了保证(即该行为是否是错误)?
  3. 有没有办法让HotSpot加载并同时加载?编写自定义类装入器是否有效?aA

答案 1
  • 有没有关于每个JVM中的引导类装入器可以装入哪些类?

语言的核心部分,以及支持的实现类。不保证包含您编写的任何类。(普通的 JVM 在独立于引导数据库的类装入器中加载您的类,实际上,普通的引导加载程序通常将其类从 JAR 中装入出来,因为这比一个充满类的大型旧目录结构更有效地部署。

  • 如果有任何保证,上面示例中的行为是否违反了保证(即该行为是否是错误)?
  • 有没有办法让“标准”JVM同时加载a和A?编写自定义类装入器是否有效?

Java 通过将类的全名映射到文件名来装入类,然后在类路径上搜索该文件名。因此,转到和转到 。一些文件系统将这些东西混为一谈,当要求一个文件系统时,它可能会为另一个文件系统服务。其他人做对了(特别是,JAR文件中使用的ZIP格式的变体是完全区分大小写的和可移植的)。Java对此无能为力(尽管IDE可以通过使文件远离本机FS来为您处理它,但我不知道是否真的这样做,JDK肯定不是那么聪明)。testcase.atestcase/a.classtestcase.Atestcase/A.class.classjavac

然而,这并不是这里唯一需要注意的一点:类文件在内部知道它们在谈论什么类。文件中缺少预期的类只是意味着加载失败,从而导致您收到。你得到的是一个问题(至少在某种意义上是错误部署),它被检测到并得到稳健的处理。从理论上讲,你可以构建一个类加载器,可以通过继续搜索来处理这些事情,但为什么要打扰呢?将类文件放在JAR中将更健壮地解决问题;这些都处理得当。NoClassDefFoundError

更一般地说,如果你经常遇到这个问题,那就在Unix上使用区分大小写的文件系统(建议使用Jenkins这样的CI系统)进行生产构建,并找到哪些开发人员正在命名具有大小写差异的类,并让他们停止,因为它非常令人困惑!


答案 2

Donal的精细解释几乎没有什么可补充的,但让我简要地思考一下这句话:

...具有相同不区分大小写名称的 Java 类...

一般来说,名称和字符串本身从不区分大小写,只有解释才能。其次,Java不会做这样的解释。

因此,您所想到的正确措辞是:

...在不区分大小写的文件系统中的文件表示形式具有相同的名称的 Java 类...


推荐