Чувствительность к классу Java-классов - программирование
Подтвердить что ты не робот

Чувствительность к классу Java-классов

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

Ошибка typechecker, но когда я запускаю файл класса, генерируя код непосредственно выше, я получаю:

Исключение в потоке "main" java.lang.NoClassDefFoundError: testcase/A (неправильное имя: testcase/a)

В Java имена в общем случае чувствительны к регистру. Некоторые файловые системы (например, Windows) нечувствительны к регистру, поэтому я не удивлен, как это происходит, но это кажется неправильным. К сожалению, спецификации Java странно неконкретны, какие классы видны. Java Language Specification (JLS), Java SE 7 Edition (раздел 6.6.1, стр. 166) гласит:

Если класс или тип интерфейса объявлены общедоступными, к нему может быть любой код, при условии, что блок компиляции (§7.3), в котором он объявлен, является наблюдаемыми.

В разделе 7.3 JLS определяет наблюдаемость единицы компиляции в чрезвычайно неопределенных терминах:

Все единицы компиляции предопределенного пакета java и его подпакетов lang и io всегда наблюдаемы. Для всех других пакетов хост-система определяет, какие единицы компиляции наблюдаемы.

Спецификация виртуальной машины Java также смутно (раздел 5.3.1):

Следующие шаги используются для загрузки и тем самым создают класс nonarray или интерфейс C, обозначенный [binary name] N, с помощью загрузчика bootstrap class [...] В противном случае виртуальная машина Java передает аргумент N вызову метод загрузчика класса bootstrap для поиска предполагаемого представления C в зависимости от платформы.

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

  • Есть ли какие-либо гарантии относительно того, какие классы загружаются загрузчиком классов по умолчанию в каждой JVM? Другими словами, могу ли я реализовать действительный, но вырожденный JVM, который не будет загружать какие-либо классы, кроме тех, что указаны в java.lang и java.io?
  • Если есть какие-либо гарантии, поведение в приведенном выше примере нарушает гарантию (т.е. является ли поведение ошибкой)?
  • Есть ли способ сделать загрузку HotSpot a и a одновременно? Будет ли писать пользовательский загрузчик классов?
4b9b3361

Ответ 1

  • Есть ли какие-либо гарантии относительно того, какие классы загружаются загрузчиком класса bootstrap в каждой JVM?

Основные биты и куски языка, а также поддерживающие классы реализации. Не гарантируется включение любого класса, который вы пишете. (Обычный JVM загружает ваши классы в отдельный загрузчик классов из начальной загрузки, и на самом деле обычный загрузчик загрузки загружает свои классы из JAR в обычном режиме, поскольку это обеспечивает более эффективное развертывание, чем большая старая структура каталогов, полная классов.)

  • Если есть какие-либо гарантии, поведение в приведенном выше примере нарушает гарантию (т.е. является ли поведение ошибкой)?
  • Есть ли способ сделать "стандартные" JVM загружать a и A одновременно? Будет ли писать пользовательский загрузчик классов?

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

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

В более общем плане, если вы столкнулись с этой проблемой очень много, возьмите для создания сборки в Unix с чувствительной к регистру файловой системе (рекомендуется CI-система, например, Jenkins), и найдите, какие разработчики называют классы с помощью просто случайные различия и заставляют их останавливаться, поскольку это очень запутывает!

Ответ 2

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

... Java-классы с тем же именем, не зависящим от регистра...

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

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

... Java-классы, представления файлов которых в файловой системе, не учитывающей регистр, имеют одинаковые имена...

Ответ 3

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

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

Когда я упоминаю "пакеты", я не имею в виду файлы "*.JAR", но только концепция:

package com.mycompany.mytool;

// "com.mycompany.mytool.MyClass"

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

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

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

Только мои 2 цента, для вашей чашки Java coffe