Подтвердить что ты не робот

Идентичные источники Java объединяются в двоичные классы

Может ли кто-нибудь объяснить, как идентичные источники Java могут завершить компиляцию в двоичные файлы разных классов?

Возникает вопрос о следующей ситуации:

Мы имеем довольно большое приложение (800+ классов), которое было разветвлено, реструктурировано, а затем снова интегрировано в багажник. До реинтеграции мы объединили багажник в филиал, который является стандартной процедурой.

Конечным результатом был набор каталогов с источниками ветвления и набор каталогов с источниками соединительных линий. Используя Beyond Compare, мы смогли определить, что оба набора источников идентичны. Однако при компиляции (тот же JDK, использующий maven, размещенный в IntelliJ v11), мы заметили, что около дюжины файлов классов были разными.

Когда мы декомпилировали источник для каждой пары явно разных файлов классов, мы оказались с одним и тем же источником java, поэтому с точки зрения конечного результата это, похоже, не имеет значения. Но почему именно несколько файлов отличаются друг от друга?

Спасибо.


Дополнительная мысль:

Если maven/javac компилирует файлы в другой последовательности, может ли это повлиять на конечный результат?

4b9b3361

Ответ 1

Предполагая, что JDK и параметры компиляции идентичны, я могу представить 5 возможных источников различий:

  • Временные метки - каждый файл класса содержит метки времени компиляции. Если вы не выполняете компиляции точно в одно и то же время, разные компиляции одного и того же файла будут иметь разные временные метки.

  • Пути исходных файлов - каждый файл класса содержит путь к исходному файлу. Если вы скомпилируете два дерева с разными путями, файлы классов будут содержать разные исходные имена путей.

  • Значения импортированных констант времени компиляции - когда класс A использует константу времени компиляции, определенную в другом классе B (см. JLS для определения "константы времени компиляции" ), значение константы включается в файл класса A. Поэтому, если вы скомпилируете A для разных версий B (с разными значениями для констант), код A, скорее всего, будет другим.

  • Различия в подписях внешних классов/методов; например если вы изменили версию зависимостей в одном из ваших файлов POM.

  • Различия в классах классов сборки могут привести к различиям в порядке, в котором найдены импортированные классы, что может привести к незначительным различиям в порядке записей в файле класса Constant Pool. Это может произойти из-за таких вещей, как:

    • файлы, отображаемые в разных порядках в каталогах внешних JAR файлов,
    • файлы скомпилированы в другом порядке из-за того, что исходные файлы находятся в другом порядке, когда ваш инструмент сборки выполняет итерацию их или
    • parallelism в сборке (если это разрешено).

Обратите внимание, что вы обычно не видите фактический порядок файлов в каталогах FS, потому что инструменты, такие как ls и dir по умолчанию, сортируют записи перед их отображением.


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

Ответ 2

Когда вы сравниваете использование без сравнения, сравнение выполняется на основе содержимого файлов. Но в процессе сборки только метка времени исходных файлов проверяется на изменение. Таким образом, ваш исходный файл изменяет дату изменения, он будет перекомпилирован.

Ответ 3

Различные JDK производят разные двоичные классы (оптимизация, но также номер версии класса). Есть также варианты компиляции (JDK может компилироваться в более старом формате или может добавлять информацию об отладке).

Ответ 4

Различные версии Java могут добавлять разные метаданные, которые часто игнорируются декомпилятором.

Я предлагаю вам использовать javap -c -v для получения дополнительной информации в файле. Если это не поможет, вы можете использовать ASMifierClassVisitor, который смотрит на каждый байт.

Ответ 5

тот же JDK может также иметь различный выход в зависимости от того, как вы компилируете. вы можете компилировать с или без отладочной информации, которую вы можете компилировать для запуска в старой версии, каждый из которых приведет к другим классам.