Когда загрузчик классов загружает интерфейсы в java? - PullRequest
0 голосов
/ 19 июня 2020

Я пытаюсь понять, как работает загрузчик классов. Я создал простое тестовое приложение и запустил его в режиме -verbose: class. У меня есть интерфейс и класс.

Интерфейс

public interface Animal {

    int h = 8;
}

Класс

public class Elephant implements Animal {

    static int staticInt;

    static void initTest(){
        System.out.println("HELLO");
    }
}

Я запускаю указанную ниже команду в основном приложении и получаю ожидаемый результат. И интерфейс, и класс загружаются в память, поскольку я получил доступ к полю stati c класса Elephant

int i = Elephant.staticInt;

[0.795s][info][class,load] classloader.test.Animal source: file:/C:/study/class-loader/out/production/class-loader/
[0.795s][info][class,load] classloader.test.Elephant source: file:/C:/study/class-loader/out/production/class-loader/

Когда я запускаю приведенный ниже код, я вижу, что загружен только класс Animal, который я тоже ожидаю.

Animal.class.getClassLoader();

[0.864s][info][class,load] classloader.test.Animal source: file:/C:/study/class-loader/out/production/class-loader/

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

//When i run the below code i only get the value of the integer but not the class loading info 
System.out.println("The integer is "  + Animal.h);

The integer is 8

Примечание: все три случая рассматривались отдельно

Ответы [ 4 ]

2 голосов
/ 19 июня 2020

Java компилятор встраивает stati c final константы (int h в интерфейсе неявно stati c и final), поэтому ваш код эффективно компилируется в это:

System.out.println("The integer is "  + 8);

Как вы видите, ссылки на Animal больше нет. Вот почему он не загружается.

1 голос
/ 19 июня 2020

По-разному загружаются интерфейсы?

Собственно, нет. (Вы получите то же самое, если попытаетесь получить доступ к полю public static final класса в другом классе.)

Как я могу получить доступ к целочисленному значению, если оно не загружено?

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

Соответствующие разделы JLS:

0 голосов
/ 19 июня 2020

Вы можете видеть, что происходит, глядя прямо на байт-код. В примере класса:

public class Test {
    public void test() {
        System.out.println(Animal.h);
    }
}

И это байт-код (показан только test() для краткости):

  // access flags 0x1
  public test()V
   L0
    LINENUMBER 3 L0
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    BIPUSH 8
    INVOKEVIRTUAL java/io/PrintStream.println (I)V
   L1
    LINENUMBER 4 L1
    RETURN
   L2
    LOCALVARIABLE this LTest; L0 L2 0
    MAXSTACK = 2
    MAXLOCALS = 1

Вы можете увидеть инструкцию BIPUSH 8, определенную в java списки инструкций байт-кода с описанием:

pu sh байт в стек как целочисленное значение

Значение 8 равно не загружается откуда-либо, потому что это константа времени компиляции и, таким образом, разрешается напрямую во время компиляции, так как она никогда не изменится.

Таким образом, ваш Animal класс никогда не будет загружен загрузчиком классов.

Также при использовании декомпилятора (я использую IntelliJ, который использует Fernflower) результирующий класс Test выглядит так:

public class Test {
    public Test() {
    }

    public void test() {
        System.out.println(8);
    }
}
0 голосов
/ 19 июня 2020

Я не помню точно, но думаю, это потому, что h - это «publi c stati c final» в интерфейсе. Это означает, что константа просто переопределяется внутри байт-кода Elephant.class, если я не ошибаюсь, что означает, что Animal на данном этапе не нужен.

...