Java 类名区分大小写
如果在不同的目录中编写两个具有相同不区分大小写名称的公共 Java 类,则这两个类在运行时都不可用。(我在Windows,Mac和Linux上用几个版本的HotSpot JVM对此进行了测试。如果还有其他JVM可以同时使用,我不会感到惊讶。例如,如果我创建了一个名为和一个命名的类,如下所示:a
A
// 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 的假定表示形式。
所有这些都导致四个按重要性降序排列的问题:
- 有没有关于每个JVM中的缺省类装入器可以装入哪些类的保证?换句话说,我能否实现一个有效但退化的JVM,除了java.lang和 java.io 中的类之外,它不会加载任何类?
- 如果有任何保证,上面示例中的行为是否违反了保证(即该行为是否是错误)?
- 有没有办法让HotSpot加载并同时加载?编写自定义类装入器是否有效?
a
A