Вопрос по jvm, java – Чувствительность к регистру имен классов Java

47

Если кто-то записывает два общедоступных Java-класса с одним и тем же именем без учета регистра в разных каталогах, то оба класса не могут использоваться во время выполнения. (Я проверил это на Windows, Mac и Linux с несколькими версиями JVM HotSpot. Я не удивлюсь, если появятся другие 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, содержащие приведенный выше код:доступно с моего сайта.

Если попробую я звонюmyCase на обоих классах вот так:

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

Проверка типов завершается успешно, но когда я запускаю файл класса, сгенерированный с помощью кода выше, я получаю:

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

В Java имена, как правило, чувствительны к регистру. Некоторые файловые системы (например, Windows) нечувствительны к регистру, поэтому я не удивлен, что вышеупомянутое поведение происходит, но, похоже,wrong, К сожалению, спецификации Java странно не связаны с тем, какие классы видны.Спецификация языка Java (JLS), Java SE 7 Edition (Раздел 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.

В Разделе 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 так же расплывчато (раздел 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.

Все это приводит к четырем вопросам в порядке убывания важности:

  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 a and A simultaneously? Would writing a custom class loader work?
@GregHewgill Вы правильно описали сценарий в начале моего вопроса, но мои вопросы более широкие. Josh Sunshine
Итак, позвольте мне получить это прямо ... у вас есть два с одинаковыми именами классовtestcase.a а такжеtestcase.A, в двоемdifferent каталоги в вашем classpath (потому что вы не можете иметь их в одном каталоге в нечувствительной к регистру файловой системе) - и вам интересно, почему JVM не может найти правильный файл класса для загрузки? Greg Hewgill
@JoshSunshine: формат файла ZIP (файлы JAR на самом деле являются файлами ZIP) можно рассматривать как чувствительную к регистру файловую систему. Greg Hewgill
Выcan загрузить обаa а такжеA одновременно. Просто поместите их в файл JAR и загрузите оттуда. Hot Licks
Сама Java полностью чувствительна к регистру. Он не может контролировать, является ли поддержка файловой системы чувствительной к регистру или нет. «Хост-система определяет, какие единицы компиляции являются наблюдаемыми». просто говорит, что JVM сама не контролирует путь к классам - не имеет никакого отношения к чувствительности к регистру. Hot Licks

Ваш Ответ

3   ответа
19
  • Are there any guarantees about which classes are loadable by the bootstrap class loader in every JVM?

Основные части языка, а также поддержка классов реализации. Не обязательно включать любой класс, который вы пишете. (Обычная 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?

Java загружает классы, сопоставляя полное имя класса с именем файла, которое затем ищется в пути к классам. таким образомtestcase.a идет кtestcase/a.class а такжеtestcase.A идет кtestcase/A.class, Некоторые файловые системы смешивают эти вещи и могут обслуживать другие, когда об этом просят. Другие понимают это правильно (в частности, вариант формата ZIP, используемый в файлах JAR, полностью чувствителен к регистру и переносим). Java ничего не может с этим поделать (хотя IDE может справиться с этим, сохранив.class файлы вне родной FS, я не знаю, если таковые вообще есть, и JDKjavac скорее всего, это не так умно).

Однако это не единственное замечание: файлы классов знают, о каком классе они говорят. Отсутствиеexpected класс из файла просто означает, что загрузка не удалась, что приводит кNoClassDefFoundError ты получил. То, что вы получили, было проблемой (неправильное развертывание, по крайней мере, в некотором смысле), которая была обнаружена и решена. Теоретически, вы могли бы создать загрузчик классов, который мог бы обрабатывать такие вещи, продолжая поиск, ноwhy bother? Помещение файлов классов в JAR исправит ситуацию гораздо надежнее; те обрабатываются правильно.

В более общем плане, если вы действительно много сталкиваетесь с этой проблемой, займитесь производственными сборками на Unix с чувствительной к регистру файловой системой (рекомендуется система CI, такая как Jenkins), иfind which developers are naming classes with just case differences and make them stop as it is very confusing!

всеjava. загружаются начальной загрузкой, остальным загрузчикам классов это строго запрещено. Это часть модели безопасности. Все остальное, что не начинается сjava. это честная игра
Это то, что я и сделал, но это делает взаимодействие между Java и другим языком гораздо более неловким. Код Java, который ссылается на классы, созданные путем компиляции кода, написанного на другом языке, должен использовать нормализованные имена вместо имен источников. Тем не менее, детали моего проекта немного не относятся к делу, я больше заинтересован в понимании описания загрузки классов в JVM-спецификации и специфического поведения JVM HotSpot. Josh Sunshine
Re: & quot; остановите их. & Quot; Я не могу! Я пишу компилятор для другого языка, который компилируется в байт-код Java. Идентификаторы в источнике должны совпадать с идентификаторами в целевом объекте (байт-код), и несколько важных примеров на другом языке имеют одинаковые имена без учета регистра. Josh Sunshine
@Josh Вы можете попытаться сохранить сгенерированные определения классов непосредственно в JAR, а не в.class файлы на родной файловой системе. JAR-файлы чувствительны к регистру внутри, поэтому это позволит избежать всех этих странных проблем, но потребует от вас настройки компилятора так, чтобы он управлял тем, как он записывает файлы.
Ну, нормальный способ справиться с такими вещами - нормализовать дело в переводе.
1

Прекрасное объяснение Донала оставляет мало, чтобы добавить, но позвольте мне кратко остановиться на этой фразе:

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

Имена и строки в общем случае никогда не чувствительны к региструin themselvesтолько тамinterpretation это может быть. И во-вторых, Java не делает такой интерпретации.

Итак, правильная формулировка того, что вы имели в виду, была бы:

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

Имя может быть нечувствительным к регистру (и я уже давно сталкивался с нечувствительными к регистру строками на странном мейнфрейме; brrr!), Но имена Java всегда чувствительны к регистру, и все строки были чувствительны к регистру для & # # x2026; ну конечно вся моя карьера хотя бы.
-2

Не думай только о папках.

Используйте явные разные пространства имен («пакеты») для своих классов и, возможно, используйте папки, соответствующие вашим классам.

Когда я упоминаю «пакеты», я не имею в виду «*. JAR»; файлы, но, просто концепция:

package com.mycompany.mytool;

// "com.mycompany.mytool.MyClass"

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

Когда вы не указываете пакет для своего кода, инструменты java (компилятор, I.D.E., что угодно) предполагают использовать один и тот же глобальный пакет для всех. И, в случае нескольких похожих классов, у них есть список папок, где их искать.

Пакеты похожи на «виртуальные» папки в вашем коде и применяются ко всем вашим пакетам на вашем classpath или установке Java. У вас может быть несколько классов с одним и тем же идентификатором, но, если они находятся в другом пакете, и вы указываете, какой пакет искать, у вас не возникнет проблем.

Просто мои 2 цента за чашку кофе Java

Похожие вопросы