Инициализация значения экземпляра по умолчанию в java

Я пытаюсь проверить значение по умолчанию для переменных экземпляра (т.е. 0 здесь) в сгенерированном байт-коде . я вижу <init>() вызывается, и если я печатаю переменную экземпляра внутри конструктора, я вижу getfield требовалось, но тогда где сначала было установлено это значение по умолчанию?

Пожалуйста, ответьте на следующие вопросы:

  1. Когда устанавливается значение по умолчанию в myvar? (по истечении времени компиляции или создания объекта)
  2. Кто (компилятор или jvm) инициализирует (или, скажем, устанавливает значение по умолчанию) в переменной экземпляра?
      public class FieldInit {

    int myvar;

    public static void main(String[] args) {

        new FieldInit(); // and what would happen if I comment out this
    }
}

Я пытаюсь разобрать байт-код, используя javapно не могу увидеть метод, я предполагаю, что это может происходить здесь. Пожалуйста, дайте мне знать, можно ли увидеть <clinit>() метод, и если да, то как?

2 ответа

В JVM создание экземпляра объекта разделено на две инструкции байт-кода:

  1. выделяет новый неинициализированный объект;
  2. invokespecial вызывает конструктор, который инициализирует объект.

Спецификация JVM для байт-кода гласит :

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

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

  1. На ваш вопрос невозможно ответить, потому что он сложнее, чем вы думаете.

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

Посмотрим на это в действии! (и обратите внимание, что L это просто синтаксис java, чтобы сообщить java: это число является long, а не int. 5 - постоянная величина, как и 5L).

      class Test {
    public static final long mark = 5L;
}

javac Test.java
javap -v c Test
       .....
public static final long mark;
   descriptor: J
   flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
   ConstantValue: long 5l

Послушайте, вот оно, постоянная стоимость 5 л.

Но что, если он не постоянный?

Ах, это проблема. Вы не можете закодировать это здесь.

Итак, вместо этого пришло время синтаксического сахара!

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

      public class Test {
    static {
        System.out.println("What voodoo magic is this?");
    }

    public static void main(String[] args) {
        System.out.println("In main");
    }
}

Посмотрим на это в действии!

       javac Test.java
java Test
What voodoo magic is this?
In main

javap -c -v Test
...
static {};
   descriptor: ()V
   flags: (0x0008) ACC_STATIC
   Code:
     stack=2, locals=0, args_size=0
        0: getstatic     #7                  // Field >java/lang/System.out:Ljava/io/PrintStream;
        3: ldc           #21                 // String Voodoo...
        5: invokevirtual #15                 // Method >java/io/PrintStream.println:(Ljava/lang/String;)V
        8: return
     LineNumberTable:
       line 3: 0
       line 4: 8
}

Как видите, эта странная штука была скомпилирована во что-то, что выглядит в точности как метод, байт-код и все такое, но название метода странное, просто static{}.

Вот и подсказка

Но что произойдет, если мы немного усложним это. Вместо этого давайте инициализируем это поле текущим временем!

      class Test {
    public static final long mark = System.currentTimeMillis();
}

Это просто синтаксический сахар. Это объясняет, как это работает, потому что, как я уже говорил, на уровне файла класса вы не можете инициализировать поля неконстантными . Таким образом, это компилируется в то же самое, что и:

      class Test {
    public static final long mark;

    static {
        mark = System.currentTimeMillis();
    }
}

и ты можешь javap чтобы подтвердить это.

Один из них называется «постоянной времени компиляции». Другой - нет. Это проявляется по-разному. Например, вы можете передать CTC в качестве параметра аннотации. Попробуйте это: попробуйте использовать static final long MARK = 5L; (вы сможете), тогда static final long MARK = System.currentTimeMillis(); - этого не допустят.

Итак, где это начальное значение? Если это постоянное значение, javap -c -vпокажет это. Если это не так, он застрял в статическом блоке.

Другие вопросы по тегам