Как это возможно, что java .lang.Object реализован в Java? - PullRequest
8 голосов
/ 10 июля 2020

Согласно спецификации языка Java , java.lang.Object - это root иерархии наследования Java. В отличие от C ++ или Objective- C, программисты не могут указывать свои собственные суперклассы root. Из-за этого я решил, что невозможно определить java.lang.Object в самом Java. К моему удивлению, я обнаружил, что OpenJDK действительно имеет конкретную реализацию java.lang.Object.

. Я хотел посмотреть, можно ли скомпилировать и запустить мою собственную версию java.lang.Object с JDK / JRE 1.8, поэтому я написал это как Object.java:

package java.lang;
public class Object {
    public static void main(String[] args) {
        System.out.println("Hello world from custom java.lang.Object!");
    }
}

Он отлично скомпилировался с javac, но если я попытаюсь выполнить файл класса через java -cp . java.lang.Object, ошибки JVM исчезнут с это сообщение:

Error: Main method not found in class java.lang.Object, please define the main method as:
   public static void main(String[] args)

, которое предполагает, что JVM использует запас java.lang.Object вместо Object.class в пути к классам.

Можно ли определить java.lang.Object в Java как конечный пользователь? Что творится под капотом? Я вижу две возможности:

  • JVM загружает настоящий Object.class файл, который был скомпилирован из Object.java OpenJDK. Если это так, делает ли компилятор что-то особенное, чтобы файл класса не определял себя как суперкласс?
  • java.lang.Object - это внутренняя c реализация JVM. Если это так, то почему в OpenJDK стоит Object.java?

Ответы [ 3 ]

6 голосов
/ 10 июля 2020

Вы можете изменить java.lang.Object (например, добавив метод public static void main()), но для загрузки и использования JVM измененный класс необходимо добавить к классу bootstrap. путь.

В JDK 8 это можно сделать с помощью

java -Xbootclasspath/p:<path>

В JDK 9+ это требует исправления java.base модуля:

java --patch-module java.base=<path>

При запуске JVM , он загружает java.lang.Object загрузчиком классов bootstrap, как и любой другой класс, поэтому java.lang.Object с добавленным методом main может быть фактически выполнено:

$ java -Xbootclasspath/p:. java.lang.Object
Hello world from custom java.lang.Object!

Однако, если вы попытаетесь удалить существующие java.lang.Object методы, добавить новые виртуальные методы, добавить поля или иным образом изменить существующий макет - это не сработает. Скорее всего, JVM просто выдаст sh с фатальной ошибкой.

Это потому, что JVM ожидает, что java.lang.Object будет иметь известную схему. В исходном коде JVM есть жестко запрограммированные смещения, ссылки на существующие методы и т. Д. c. То же самое верно и для других встроенных классов c, таких как java.lang.String, java.lang.Class, java.lang.ref.Reference и т.п. 1030 *:

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

И компилятор Java, и JVM знают об этом исключении и применяют это правило при компиляции Object.java и при загрузке Object.class.

6 голосов
/ 10 июля 2020

Вы можете реализовать java.lang.Object в Java, и фактический класс, который вы используете, действительно был создан из файла Object.java, который поставляется с JDK.

Спецификация языка Java® говорит в Глава 8. Классы :

Каждый класс, кроме Object, является расширением (то есть подклассом) одного существующего класса ( §8.1.4 ) и может реализовывать интерфейсы ( §8.1.5 ).

Таким образом, отсутствие супертипов для Object исправлено в языке.

Вы можете использовать исходный код вашего эксперимента и попробовать добавить предложение extends или implements и увидеть, что компилятор отклонит его.

Когда вы компилируете класс java.lang.Object, полученный класс файл будет единственным, у которого нет супертипа. См. Спецификацию виртуальной машины Java®, §4.1., Структура ClassFile :

super_class

Для класса значение элемента super_class должно быть либо нулем, либо допустимым индексом в таблице constant_pool. Если значение элемента super_class не равно нулю, запись constant_pool в этом индексе должна быть структурой CONSTANT_Class_info, представляющей прямой суперкласс класса, определенного этим файлом class. Ни у прямого суперкласса, ни у любого из его суперклассов не может быть установлен флаг ACC_FINAL в элементе access_flags его структуры ClassFile.

Если значение элемента super_class равно нулю, то это Файл class должен представлять класс Object, единственный класс или интерфейс без прямого суперкласса.

Для интерфейса значение элемента super_class всегда должно быть допустимым индексом в constant_pool стол. Запись constant_pool в этом индексе должна быть структурой CONSTANT_Class_info, представляющей класс Object.

Таким образом, даже интерфейсы имеют запись для суперкласса в файле класса (указывает на Object), а файл класса для java.lang.Object является единственным файлом с нулевой записью для суперкласса.

Когда вы пытаетесь загрузить свою версию класса Object во время выполнения, вы сталкиваетесь с тем фактом, что вы не можете загрузить классы из пакета java.lang (или любого класса, полное имя которого начинается с java.) через путь к классам в целом.

До Java 9 вам нужно было настроить bootstrap путь к классам для включения вашей версии. Начиная с Java 9, класс java.lang.Object должен принадлежать к java.base модулю , который загружается в соответствии с особенностями реализации c. Вам нужно будет использовать параметр --patch-module, чтобы внедрить вашу собственную версию.

Но вы должны быть осторожны с тем, что вы пишете в своей собственной версии. Другие классы и среда возлагают большие надежды на другие классы, и их несоблюдение может нарушить их (плохо).

JLS, §4.3.2. В объекте класса перечислены ожидаемые методы и ссылки на другие главы, которые определяют семантику специального языка для некоторых из них.

3 голосов
/ 10 июля 2020

Это действительно крутой эксперимент. Но вот как работает Java

  1. Поскольку каждый класс в Java должен расширять java.lang.Object, ваш собственный Object класс также расширяет это.
  2. Чтобы загрузить любой class, Java необходимо загрузить родительские классы. Поэтому, когда Java пытается запустить метод main() внутри вашего настраиваемого Object класса, он загружает реальный java.lang.Object класс.
  3. Как только загружается реальный java.lang.Object класс, JVM пытается выполнить запустите метод main() этого класса. Поскольку его не существует, ваше приложение выдает ошибку.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...