Java类名的区分大小写
如果在不同的目录中写入两个具有相同大小写不敏感的公共Java类,那么这两个类在运行时就不可用。 (我在Windows,Mac和Linux上用HotSpot JVM的几个版本testing过,如果有其他的JVM在同一时间使用,我不会感到惊讶)。例如,如果我创build一个名为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());
typechecker成功,但是当我直接运行上面的代码生成的类文件时,我得到:
线程“main”中的exceptionjava.lang.NoClassDefFoundError:testcase / A(错误名称:testcase / a)
在Java中,名称通常是区分大小写的。 一些文件系统(如Windows)不区分大小写,所以我不惊讶上述行为发生,但似乎是错误的 。 不幸的是,Java规范很奇怪,不能确定哪些类是可见的。 Java语言规范(JLS),Java SE 7版本 (第166页的6.6.1节)说:
如果一个类或接口types被声明为public,那么它可以被任何代码访问,只要它被声明的编译单元(§7.3)是可观察的。
在第7.3节中,JLS定义了一个非常模糊的编制单位的可观察性:
预定义的Java包及其子包lang和io的所有编译单元都是可观察的。 对于所有其他包, 主机系统确定哪些编译单元是可观察的 。
Java虚拟机规范同样含糊(5.3.1节):
下面的步骤用于加载并由此使用引导类加载器[…]来创build由[二进制名称] N表示的非数组类或接口C。否则,Java虚拟机将参数N传递给调用方法引导类加载器以平台相关的方式searchC的声称表示。
所有这些导致四个问题按照重要性的降序排列:
- 每个JVM中的默认类加载器可以加载哪些类可以加载吗? 换句话说,我可以实现一个有效的,但退化的JVM,不会加载任何类,除了在java.lang和java.io中的类吗?
- 如果有任何保证,上面的例子中的行为是否违反了保证(即行为是一个错误)?
- 有没有办法让HotSpot同时加载
a
和A
? 会写一个自定义类加载器的工作?
- 在每个JVM中,有没有关于哪些类可以通过引导类加载器加载的保证?
语言的核心部分,以及支持的实现类。 不保证包括你写的任何课程。 (普通的JVM将类加载到一个独立的类加载器中,而实际上,普通的引导加载器通常将其类从JAR中加载出来,因为这样比部署一个大的旧的目录结构更有效。
- 如果有任何保证,上面的例子中的行为是否违反了保证(即行为是一个错误)?
- 有没有办法让“标准”的JVM同时加载一个和A? 会写一个自定义类加载器的工作?
Java通过将类的全名映射到文件名中加载类,然后在类path中search该文件名。 因此, testcase.a
进入testcase/a.class
和testcase.A
进入testcase/A.class
。 有些文件系统把这些东西混合起来,并且在需要的时候可以为另一个文件系统提供服务。 其他人则正确(特别是JAR文件中使用的ZIP格式的变体是完全区分大小写和便携的)。 Java没有办法做到这一点(尽pipeIDE可以通过保持.class
文件远离本地FS来处理它,但我不知道是否真的有这么做,JDK的javac
肯定不是那么聪明)。
然而,这不是唯一需要注意的地方:类文件在内部知道他们正在讨论的是什么类。 文件中没有预期的类只是意味着加载失败,导致您收到NoClassDefFoundError
。 你得到的是一个问题(至less在某种意义上是错误的部署),被强烈检测和处理。 从理论上讲,你可以build立一个类加载器,通过继续search来处理这些事情,但是为什么呢? 将类文件放在JAR中会解决更强大的问题。 那些处理正确。
更一般地说,如果你真的遇到了这个问题,可以考虑在Unix上使用区分大小写的文件系统(build议使用像Jenkins这样的CI系统)进行生产构build,并找出哪些开发人员命名类只是区分大小写并让他们停下来,因为它是非常混乱!
多纳的好的解释没有多less补充,但让我简单地思考一下这句话:
…具有相同大小写不敏感名称的Java类
一般来说名称和string从来都不区分大小写,只有解释才可以。 其次,Java不做这样的解释。
所以,你想到的一个正确的措辞是:
在不区分大小写的文件系统中,其文件表示forms的Java类具有相同的名称…
不要只考虑文件夹。
使用显式的不同名称空间(“包”)为您的类,也许使用文件夹来匹配您的类。
当我提到“软件包”时,我并不是指“* .JAR”文件,而只是以下概念:
package com.mycompany.mytool; // "com.mycompany.mytool.MyClass" public class MyClass { // ... } // class MyClass
当你没有为你的代码指定一个包的时候,java工具(编译器,IDE等等)假定为所有的包都使用相同的全局包。 而且,在几个类似的情况下,他们有一个文件夹列表,在哪里寻找。
包就像代码中的“虚拟”文件夹一样,并且适用于您的类path上的所有包,或者Java的安装。 您可以拥有几个具有相同ID的类,但是,如果它们位于不同的包中,并指定要查找的包,则不会有任何问题。
只是我的2美分,为你的一杯Java咖啡