Java类名的区分大小写
如果一个人在不同目录中编写了两个具有相同的不区分大小写名称的公共Java类,则这两个类在运行时将不可用。(我在Windows,Mac和Linux上使用多个版本的HotSpot
JVM进行了测试。如果还有其他同时可用的JVM,我也不会感到惊讶。)例如,如果我创建了a
一个名为类的类,则A
这样命名:
// lowercase/src/testcase/a.javapackage 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());
类型检查器成功,但是当我运行直接由上面代码生成的类文件时,我得到:
线程“主”中的异常java.lang.NoClassDefFoundError:testcase / A(错误名称:testcase / a)
在Java中,名称通常区分大小写。某些文件系统(例如Windows)不区分大小写,因此上述行为不会使我感到惊讶,但这似乎是 错误的
。不幸的是,关于哪些类是可见的,Java规范是奇怪的。在Java语言规范(JLS)的Java SE
7版(第6.6.1节,第166页)说:
如果将类或接口类型声明为公共,则只要可以观察到声明了其的编译单元(第7.3节),任何代码都可以访问它。
在7.3节中,JLS用非常模糊的术语定义了编译单元的可观察性:
预定义包java及其子包lang和io的所有编译单元始终是可观察的。对于所有其他软件包, 。
在Java虚拟机规范同样是模糊的(第5.3.1节):
下列步骤用于加载,从而使用引导类加载器创建由[二进制名称] N表示的非数组类或接口C
[…]否则,Java虚拟机将参数N传递给在以下位置调用方法引导类加载器以依赖于平台的方式搜索C的声称表示。
所有这些都导致四个问题的重要性降序:
- 是否可以保证每个JVM中的默认类加载器可以加载哪些类?换句话说,我是否可以实现一个有效的但退化的JVM,它不会加载java.lang和java.io中的类之外的任何类?
- 如果有任何保证,那么上面示例中的行为是否违反了保证(即行为是否是错误)?
- 有没有什么办法,使热点负载
a
和A
同步?编写自定义类加载器会起作用吗?
回答:
- 是否可以保证每个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中可以更牢固地解决问题。那些被正确处理。
更一般而言,如果您确实确实遇到了这个问题,请在具有区分大小写的文件系统(建议使用Jenkins等CI系统)的Unix上进行生产构建,并
找出哪些开发人员在命名时区分大小写并
以上是 Java类名的区分大小写 的全部内容, 来源链接: utcz.com/qa/409312.html