Java 类名区分大小写

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10890805/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 02:59:52  来源:igfitidea点击:

Case sensitivity of Java class names

javajvmjls

提问by Josh Sunshine

If one writes two public Java classes with the same case-insensitive name in different directories then both classes are not usable at runtime. (I tested this on Windows, Mac and Linux with several versions of the HotSpot JVM. I would not be surprised if there other JVMs where they are usable simultaneously.) For example, if I create a class named aand one named Alike so:

如果在不同的目录中使用相同的不区分大小写的名称编写两个公共 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";
    }
}

Three eclipse projects containing the code above are available from my website.

包含上述代码的三个 eclipse 项目可从我的网站获得

If try I calling myCaseon both classes like so:

如果尝试我myCase像这样调用两个类:

System.out.println(A.myCase());
System.out.println(a.myCase());

The typechecker succeeds, but when I run the class file generate by the code directly above I get:

类型检查器成功了,但是当我运行由上面的代码生成的类文件时,我得到:

Exception in thread "main" java.lang.NoClassDefFoundError: testcase/A (wrong name: testcase/a)

线程“main”中的异常java.lang.NoClassDefFoundError:testcase/A(错误名称:testcase/a)

In Java, names are in general case sensitive. Some file systems (e.g. Windows) are case insensitive, so I'm not surprised the above behavior happens, but it seems wrong. Unfortunately the Java specifications are oddly non-commital about which classes are visible. The Java Language Specification (JLS), Java SE 7 Edition(Section 6.6.1, page 166) says:

在 Java 中,名称通常区分大小写。一些文件系统(例如 Windows)不区分大小写,所以我对上述行为的发生并不感到惊讶,但它似乎是错误的。不幸的是,Java 规范奇怪地没有明确规定哪些类是可见的。在Java语言规范(JLS)的Java SE 7版(第6.6.1节,第166页)说:

If a class or interface type is declared public, then it may be accessed by any code, provided that the compilation unit (§7.3) in which it is declared is observable.

如果一个类或接口类型被声明为 public,那么它可以被任何代码访问,只要声明它的编译单元(第 7.3 节)是可观察的。

In Section 7.3, the JLS defines observability of a compilation unit in extremely vague terms:

在第 7.3 节中,JLS 以极其模糊的术语定义了编译单元的可观察性:

All the compilation units of the predefined package java and its subpackages lang and io are always observable. For all other packages, the host system determines which compilation units are observable.

预定义包 java 及其子包 lang 和 io 的所有编译单元始终是可观察的。对于所有其他包,主机系统确定哪些编译单元是可观察的

The Java Virtual Machine Specificationis similarly vague (Section 5.3.1):

Java虚拟机规范同样是模糊的(第5.3.1节):

The following steps are used to load and thereby create the nonarray class or interface C denoted by [binary name] N using the bootstrap class loader [...] Otherwise, the Java virtual machine passes the argument N to an invocation of a method on the bootstrap class loader to search for a purported representation of C in a platform-dependent manner.

以下步骤用于加载并由此创建由 [二进制名称] N 表示的非数组类或接口 C 使用引导类加载器 [...] 否则,Java 虚拟机将参数 N 传递给对方法的调用引导类加载器以依赖于平台的方式搜索 C 的声称表示。

All of this leads to four questions in descending order of importance:

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

  1. Are there any guarantees about which classes are loadable by the default class loader(s) in every JVM? In other words, can I implement a valid, but degenerate JVM, that won't load any classes except those in java.lang and java.io?
  2. If there are any guarantees, does the behavior in the example above violate the guarantee (i.e. is the behavior a bug)?
  3. Is there any way to make HotSpot load aand Asimultaneously? Would writing a custom class loader work?
  1. 对于每个 JVM 中的默认类加载器可加载哪些类,是否有任何保证?换句话说,我可以实现一个有效但退化的 JVM,它不会加载除 java.lang 和 java.io 中的类之外的任何类吗?
  2. 如果有任何保证,上例中的行为是否违反了保证(即该行为是否为错误)?
  3. 有没有什么办法,使热点负载aA同步?编写自定义类加载器会起作用吗?

采纳答案by Donal Fellows

  • Are there any guarantees about which classes are loadable by the bootstrap class loader in every JVM?
  • 是否可以保证每个 JVM 中的引导类加载器可以加载哪些类?

The core bits and pieces of the language, plus supporting implementation classes. Not guaranteed to include any class that you write. (The normal JVM loads your classes in a separate classloader from the bootstrap one, and in fact the normal bootstrap loader loads its classes out of a JAR normally, as this makes for more efficient deployment than a big old directory structure full of classes.)

语言的核心部分,以及支持的实现类。不保证包含您编写的任何类。(普通 JVM 在与引导程序不同的类加载器中加载您的类,实际上普通引导程序加载器通常从 JAR 加载其类,因为这比充满类的大型旧目录结构更有效地部署。)

  • If there are any guarantees, does the behavior in the example above violate the guarantee (i.e. is the behavior a bug)?
  • Is there any way to make "standard" JVMs load a and A simultaneously? Would writing a custom class loader work?
  • 如果有任何保证,上例中的行为是否违反了保证(即该行为是否为错误)?
  • 有没有办法让“标准”JVM 同时加载 a 和 A?编写自定义类加载器会起作用吗?

Java loads classes by mapping the full name of the class into a filename that is then searched for on the classpath. Thus testcase.agoes to testcase/a.classand testcase.Agoes to testcase/A.class. Some filesystems mix these things up, and may serve the other up when one is asked for. Others get it right (in particular, the variant of the ZIP format used in JAR files is fully case-sensitive and portable). There is nothing that Java can do about this (though an IDE could handle it for you by keeping the .classfiles away from the native FS, I don't know if any actually do and the JDK's javacmost certainly isn't that smart).

Java 通过将类的全名映射到文件名来加载类,然后在类路径中搜索该文件名。因此testcase.atestcase/a.classtestcase.Atestcase/A.class。一些文件系统将这些东西混合在一起,并且在需要时可能会为另一个文件系统提供服务。其他人做对了(特别是,JAR 文件中使用的 ZIP 格式的变体完全区分大小写且可移植)。Java 对此无能为力(尽管 IDE 可以通过使.class文件远离本机 FS来为您处理它,但我不知道是否真的这样做了,而且 JDKjavac肯定没有那么聪明)。

However that's not the only point to note here: class files know internally what class they are talking about. The absence of the expectedclass from the file just means that the load fails, leading to the NoClassDefFoundErroryou received. What you got was a problem (a mis-deployment in at least some sense) that was detected and dealt with robustly. Theoretically, you could build a classloader that could handle such things by keeping searching, but why bother?Putting the class files inside a JAR will fix things far more robustly; those are handled correctly.

然而,这并不是这里唯一需要注意的点:类文件在内部知道它们在谈论什么类。文件中缺少预期的类仅意味着加载失败,导致NoClassDefFoundError您收到。你得到的是一个问题(至少在某种意义上是错误的部署),它被检测到并得到了有力的处理。从理论上讲,您可以构建一个类加载器,通过不断搜索来处理此类事情,但何必呢?将类文件放在 JAR 中将更有效地修复问题;那些被正确处理。

More generally, if you're running into this problem for real a lot, take to doing production builds on a Unix with a case-sensitive filesystem (a CI system like Jenkins is recommended) and find which developers are naming classes with just case differences and make them stop as it is very confusing!

更一般地说,如果您经常遇到这个问题,请在具有区分大小写的文件系统的 Unix 上进行生产构建(推荐使用像 Jenkins 这样的 CI 系统),并找出哪些开发人员在命名类时只区分大小写并让他们停下来,因为这非常令人困惑!

回答by Tillmann

Donal's fine explanation leaves little to add, but let me briefly muse on this phrase:

多纳尔的精彩解释没有什么可补充的,但让我简要地思考一下这句话:

... Java classes with the same case-insensitive name ...

...具有相同的不区分大小写名称的 Java 类 ...

Names and Strings in general are never case-insensitive in themselves, it's only there interpretationthat can be. And secondly, Java doesn't do such an interpretation.

通常,名称和字符串本身从不区分大小写只能进行解释。其次,Java 不做这样的解释。

So, a correct phrasing of what you had in mind would be:

因此,您所想到的正确措辞是:

... Java classes whose file representations in a case-insensitive file-system have identical names ...

... Java 类在不区分大小写的文件系统中的文件表示具有相同的名称 ...

回答by umlcat

Don't think just about folders.

不要只考虑文件夹。

Use explicit different namespaces ("packages") for your classes, and maybe use folders to match your classes.

为您的类使用显式不同的命名空间(“包”),并且可能使用文件夹来匹配您的类。

When I mention "packages", I don't mean "*.JAR" files, but, just the concept of:

当我提到“包”时,我不是指“*.JAR”文件,而是指以下概念:

package com.mycompany.mytool;

// "com.mycompany.mytool.MyClass"

public class MyClass
{
   // ...
} // class MyClass

When you do not specify a package for your code, the java tools (compiler, I.D.E., whatever), assume to use the same global package for all. And, in case of several similar classes, they have a list of folders, where to look for.

当您没有为代码指定包时,java 工具(编译器、IDE 等)假定对所有工具使用相同的全局包。并且,如果有几个相似的类,它们有一个文件夹列表,可以在何处查找。

Packages are like "virtual" folders in your code, and apply to all your packages on your classpath, or installation of Java. You can have several classes, with the same I.D., but, if they are in different package, and you specify which package to look for, you won't have any problem.

包就像代码中的“虚拟”文件夹,适用于类路径或 Java 安装中的所有包。您可以有多个具有相同 ID 的类,但是,如果它们位于不同的包中,并且您指定要查找的包,则不会有任何问题。

Just my 2 cents, for your cup of Java coffe

只需我的 2 美分,即可享用一杯 Java 咖啡