Чувствительность к регистру имен классов Java
если один пишет два открытых класса Java с одинаковым именем без учета регистра в разных каталогах, то оба класса не могут использоваться во время выполнения. (Я протестировал это на Windows, Mac и Linux с несколькими версиями HotSpot JVM. Я бы не удивился, если бы были другие JVMs, где они могут использоваться одновременно.) Например, если я создам класс с именем 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 " java.ленг.NoClassDefFoundError: testcase/A (неверное название: testcase/a)
в Java имена в целом чувствительны к регистру. Некоторые файловые системы (например, Windows) нечувствительны к регистру, поэтому я не удивительно, что такое поведение происходит, но кажется неправильно. К сожалению, спецификации Java странно не связаны с тем, какие классы видны. The спецификация языка Java (JLS), Java SE 7 Edition (раздел 6.6.1, стр. 166) говорит:
если класс или тип интерфейса объявлен открытым, то он может быть доступен любой код, при условии, что единица компиляции (§7.3), в котором он объявлен заметный.
в разделе 7.3 JLS определяет наблюдаемость единицы компиляции в чрезвычайно расплывчатых терминах:
все единицы компиляции предопределенного пакета java и его подпакетов lang и Ио всегда наблюдаемы. Для всех остальных пакетов, хост-система определяет, какие единицы компиляции заметны.
на Спецификация Виртуальной Машины Java аналогично расплывчато (раздел 5.3.1):
выполните следующие действия, чтобы загрузить и тем самым создать класс немассива или интерфейс C обозначается [двоичное имя] N с помощью загрузчика классов bootstrap [...] В противном случае виртуальная машина Java передает аргумент N вызову a способ на загрузчике класса bootstrap для поиска предполагаемого представления C в зависимости от платформы.
все это приводит к четырем вопросам в порядке убывания важность:
- существуют ли какие-либо гарантии того, какие классы загружаются загрузчиком(загрузчиками) классов по умолчанию в каждой JVM? Другими словами, могу ли я реализовать допустимый, но вырожденный JVM, который не будет загружать никакие классы, кроме классов в java.Лэнг и Ява.Ио?
- если есть какие-либо гарантии, нарушает ли поведение в приведенном выше примере гарантию (т. е. является ли поведение ошибкой)?
- есть ли способ сделать загрузку HotSpot
a
иA
одновременно? Будет ли работать пользовательский загрузчик классов?
3 ответов
- есть ли какие-либо гарантии о том, какие классы загружаются загрузчиком класса bootstrap в каждой JVM?
основные биты и фрагменты языка, а также вспомогательные классы реализации. Не гарантируется включение любого класса, который вы пишете. (Обычный JVM загружает ваши классы в отдельном загрузчике классов из загрузчика, и на самом деле обычный загрузчик загрузчика загружает свои классы из JAR нормально, так как это делает больше эффективное развертывание, чем большая старая структура каталогов, полная классов.)
- если есть какие-либо гарантии, нарушает ли поведение в приведенном выше примере гарантию (т. е. является ли поведение ошибкой)?
- есть ли способ заставить "стандартные" JVMs загружать a и A одновременно? Будет ли работать пользовательский загрузчик классов?
Java загружает классы, сопоставляя полное имя класса с именем файла, который затем выполняется поиск на пути к классу. Таким образом testcase.a
идет testcase/a.class
и testcase.A
идет testcase/A.class
. Некоторые файловые системы смешивают эти вещи и могут обслуживать другие, когда их просят. Другие понимают это правильно (в частности, вариант формата ZIP, используемый в файлах JAR, полностью чувствителен к регистру и переносим). Нет ничего, что Java может сделать с этим (хотя IDE может справиться с этим для вас, сохраняя .class
файлы вдали от родной FS, я не знаю, действительно ли они делают и JDK javac
большинство конечно, это не так умно).
однако это не единственный момент, чтобы отметить здесь:файлы классов знают внутренне, о каком классе они говорят. Отсутствие ожидается класс из файла просто означает, что загрузка не выполняется, что приводит к NoClassDefFoundError
вы получили. То, что вы получили, было проблемой (неправильное развертывание, по крайней мере, в некотором смысле), которая была обнаружена и решена надежно. Теоретически вы можете создать classloader, который мог бы обрабатывать такие вещи, сохраняя поиск, но зачем? помещение файлов классов в банку исправит вещи гораздо более надежно; они обрабатываются правильно.
в более общем плане, если вы столкнулись с этой проблемой на самом деле много, возьмите на себя выполнение производственных сборок на Unix с чувствительной к регистру файловой системой (рекомендуется система CI, такая как Дженкинс) и найти, какие разработчики именуют классы с только разницей случаев и остановить их, так как это очень путаешь!
прекрасное объяснение Донала мало что оставляет добавить, но позвольте мне кратко поразмышлять над этой фразой:
... Классы Java с тем же именем без учета регистра ...
имена и строки вообще никогда не нечувствительны к регистру сами по себе, это только там толкование что может быть. И, во-вторых, Java не делает такой интерпретации.
Итак, правильная формулировка того, что вы имели в виду, будет:
... Классы Java, файловые представления которых в файловой системе без учета регистра имеют одинаковые имена ...
Не думайте только о папках.
используйте явные различные пространства имен ("пакеты") для ваших классов и, возможно, используйте папки для соответствия вашим классам.
когда я упоминаю "пакеты", я не имею в виду"*.JAR " файлы, но, только понятие:
package com.mycompany.mytool;
// "com.mycompany.mytool.MyClass"
public class MyClass
{
// ...
} // class MyClass
когда вы не указываете пакет для своего кода, инструменты java (компилятор, I. D. E., что угодно) предполагают использовать один и тот же глобальный пакет для всех. И, в случае нескольких подобных классов, у них есть список папки, где искать.
пакеты похожи на" виртуальные " папки в вашем коде и применяются ко всем вашим пакетам на вашем пути к классам или установке Java. У вас может быть несколько классов с одинаковым идентификатором, но если они находятся в разных пакетах, и вы укажете, какой пакет искать, у вас не будет никаких проблем.
только мои 2 цента, для вашей чашки кофе Java