Заглавные буквы и NoClassDefFoundError против ClassNotFoundException

Я вижу различия между платформами в отношении того, когда Class.forName() выдает ClassNotFoundException и когда выдает NoClassDefFoundError. Это поведение где-то четко определено, или я наткнулся на ошибку?

Рассмотрим следующий код (который является автономным java-файлом в пакете по умолчанию):

public class DLExceptionType {

  private static void printFindError(String name) {
    System.out.print(name + ": ");
    try {
      Class.forName(name);
      System.out.println("** no error **");
    } catch (Throwable e) {
      System.out.println(e);
    }
  }

  public static void main(String[] args) {
    printFindError("DLExceptionType");
    printFindError("dLExceptionType"); // note the mis-capitalization
  }
}

Код выдает ожидаемый результат в Linux:

[eos18:~]$ java -version DLExceptionType
java version "1.6.0_26"
Java(TM) SE Runtime Environment (build 1.6.0_26-b03)
Java HotSpot(TM) 64-Bit Server VM (build 20.1-b02, mixed mode)
[eos18:~]$ java DLExceptionType
DLExceptionType: ** no error **
dLExceptionType: java.lang.ClassNotFoundException: dLExceptionType

Он производит другой, но понятный вывод в Windows:

java version "1.7.0_01"
Java(TM) SE Runtime Environment (build 1.7.0_01-b08)
Java HotSpot(TM) Client VM (build 21.1-b02, mixed mode, sharing)

Y:\Temp>java DLExceptionType
DLExceptionType: ** no error **
dLExceptionType: java.lang.NoClassDefFoundError: dLExceptionType (wrong name:  DLExceptionType)

Вывод в Windows имеет смысл: поскольку файловая система не чувствительна к регистру, JVM загружает файл dLExceptionType.class, но этот файл содержит класс с другим именем: DLExceptionType.

Однако, когда я запускаю код на Mac (с файловой системой с учетом регистра и более новой JVM, чем в Linux), я получаю тот же результат, что и Windows:

$ java -version
java version "1.6.0_29"
Java(TM) SE Runtime Environment (build 1.6.0_29-b11-402-10M3527)
Java HotSpot(TM) 64-Bit Server VM (build 20.4-b02-402, mixed mode)
$ java DLExceptionType
DLExceptionType: ** no error **
dLExceptionType: java.lang.NoClassDefFoundError: dLExceptionType (wrong name: DLExceptionType)

person Zack    schedule 12.11.2011    source источник
comment
На днях у меня возникла проблема с файлом репозитория git; в OS X он рассматривал foo.rb и Foo.rb как один и тот же файл. Баш знал разницу.   -  person Dave Newton    schedule 12.11.2011


Ответы (2)


HFS+ (Mac Extended) обычно не чувствителен к регистру. Начиная с Mac OS 10.3 Apple представила HFSX, который может быть чувствителен к регистру (но не по умолчанию). Если вы не указали эту опцию при инициализации диска, то ваш том, скорее всего, нечувствителен к регистру.

См.: http://en.wikipedia.org/wiki/HFS_Plus.

person Matteo    schedule 12.11.2011
comment
Ух ты. Я использую Mac в течение 8 лет и никогда не замечал, что FS не чувствителен к регистру! - person Zack; 12.11.2011

Здесь нет ошибки. То, что вы видите, полностью вызвано чувствительностью файловой системы к регистру.

При загрузке класса dLExceptionType в файловую систему без учета регистра JVM может найти файл dLException.class. Однако класс, который он содержит, не имеет имени dLException, и это приводит к ошибке NoClassDefFoundError. При попытке загрузить этот класс в чувствительной к регистру файловой системе JVM вообще не может найти файл.

NoClassDefFoundError с (wrong name: ...) в сообщении генерируется JVM всякий раз, когда она загружает класс, имя которого отличается от имени файла, из которого он был прочитан. Вы можете попробовать это в любой файловой системе, переименовав файл класса и попытавшись запустить его.

person Luke Woodward    schedule 12.11.2011