如果在不同的目录中将两个具有相同的不区分大小写的公共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项目是available from my website。
如果我尝试在这两个类上调用myCase
,请执行以下操作:
System.out.println(A.myCase());
System.out.println(a.myCase());
typechecker成功,但是当我运行由上面的代码生成的类文件时,我得到:
线程“main”中的异常java.lang.NoClassDefFoundError:testcase / A(错误名称:testcase / a)
在Java中,名称通常区分大小写。某些文件系统(例如Windows)不区分大小写,因此上述行为发生时我并不感到惊讶,但似乎错误。不幸的是,Java规范对于哪些类是可见的是奇怪的非常规。 Java Language Specification (JLS), Java SE 7 Edition(第6.6.1节,第166页)说:
如果一个类或接口类型被声明为public,那么它可以被访问 任何代码,只要声明它的编译单元(第7.3节) 观察到。
在7.3节中,JLS以极其模糊的术语定义了编译单元的可观察性:
预定义包java及其子包lang的所有编译单元 和io总是可以观察到的。对于所有其他包,主机系统确定哪些编译单元是可观察的。
Java Virtual Machine Specification同样含糊不清(第5.3.1节):
以下步骤用于加载,从而创建非阵列类或 接口C由[二进制名称] N表示,使用bootstrap类加载器[...] 否则,Java虚拟机将参数N传递给a的调用 bootstrap类加载器上的方法,用于搜索C语言的声称表示 以平台为依据。
所有这些导致四个问题按重要性降序排列:
a
和A
?编写自定义类加载器会起作用吗?答案 0 :(得分:19)
- 是否可以保证每个JVM中的引导类加载器可以加载哪些类?
语言的核心部分,以及支持实现类。不保证包括您编写的任何课程。 (普通的JVM在一个单独的类加载器中将类加载到引导程序中,实际上正常的引导加载程序通常会从JAR加载它的类,因为这样可以比一个充满类的大型旧目录结构更有效地进行部署。)
- 如果有任何保证,上述示例中的行为是否违反保证(即行为是否为错误)?
- 有没有办法让“标准”JVM同时加载a和A?编写自定义类加载器会起作用吗?
Java通过将类的全名映射到文件名来加载类,然后在类路径中搜索该文件名。因此testcase.a
转到testcase/a.class
,testcase.A
转到testcase/A.class
。有些文件系统将这些东西混合在一起,并且可以在需要时提供另一个文件系统。其他人做得对(特别是,JAR文件中使用的ZIP格式的变体完全区分大小写并且是可移植的)。 Java无法做到这一点(尽管IDE可以通过保持.class
文件远离本机FS来为您处理它,我不知道是否有任何实际操作和JDK的javac
当然不是那么聪明。
然而,这不是唯一需要注意的地方:类文件在内部知道他们在谈论什么类。文件中缺少期望的类只意味着加载失败,导致您收到NoClassDefFoundError
。你得到的是一个问题(至少在某种意义上的错误部署),它被强有力地检测和处理。从理论上讲,你可以通过保持搜索来构建一个可以处理这类事情的类加载器,但是为什么要这么麻烦?将类文件放在JAR里面会更加强大。这些都得到了正确处理。
更一般地说,如果你真的遇到了这个问题,那么在带有区分大小写的文件系统的Unix上进行生产构建(建议使用像Jenkins这样的CI系统),并且找到哪些开发人员是命名类只有大小写差异而让它们停止,因为它非常混乱!
答案 1 :(得分:1)
Donal的精细解释几乎没有什么可补充的,但让我简单介绍一下这句话:
...具有相同不区分大小写的名称的Java类...
一般来说,名称和字符串本身并不区分大小写 ,只有解释才可以。其次,Java没有做这样的解释。
因此,正确的措辞是:
...在不区分大小写的文件系统中具有相同名称的文件表示的Java类...
答案 2 :(得分:-2)
不要只考虑文件夹。
为您的类使用显式的不同命名空间(“packages”),也可以使用文件夹来匹配您的类。
当我提到“包”时,我不是指“* .JAR”文件,而是仅仅是这个概念:
package com.mycompany.mytool;
// "com.mycompany.mytool.MyClass"
public class MyClass
{
// ...
} // class MyClass
当你没有为你的代码指定一个包时,java工具(编译器,I.D.E。,无论如何)假设对所有人使用相同的全局包。并且,在几个类似的类的情况下,他们有一个文件夹列表,在哪里寻找。
包类似于代码中的“虚拟”文件夹,适用于类路径上的所有包或Java的安装。您可以使用相同的I.D.来创建多个类,但是,如果它们位于不同的包中,并且您指定要查找的包,则不会有任何问题。
只需2美分,就像你的一杯咖啡一样