Java类名称的区分大小写

时间:2012-06-05 02:26:15

标签: java jvm jls

如果在不同的目录中将两个具有相同的不区分大小写的公共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语言的声称表示   以平台为依据。

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

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

3 个答案:

答案 0 :(得分:19)

  
      
  • 是否可以保证每个JVM中的引导类加载器可以加载哪些类?
  •   

语言的核心部分,以及支持实现类。不保证包括您编写的任何课程。 (普通的JVM在一个单独的类加载器中将类加载到引导程序中,实际上正常的引导加载程序通常会从JAR加载它的类,因为这样可以比一个充满类的大型旧目录结构更有效地进行部署。)

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

Java通过将类的全名映射到文件名来加载类,然后在类路径中搜索该文件名。因此testcase.a转到testcase/a.classtestcase.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美分,就像你的一杯咖啡一样