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

Странное поведение с использованием фигурных скобок в Java

Когда я запускаю следующий код:

public class Test {

  Test(){
    System.out.println("1");
  }

  {
    System.out.println("2");
  }

  static {
    System.out.println("3");
  }

  public static void main(String args[]) {
    new Test();
  }
}

Я ожидаю получить результат в следующем порядке:

1
2
3

но я получил обратный порядок:

3
2
1

Может кто-нибудь объяснить, почему он выводится в обратном порядке?

================

Кроме того, когда я создаю более одного экземпляра Test:

new Test();
new Test();
new Test();
new Test();

статический блок выполняется только в первый раз.

4b9b3361

Ответ 1

Все зависит от порядка выполнения операторов инициализации. Ваш тест показывает, что этот порядок:

  • Статические блоки инициализации
  • Блоки инициализации экземпляра
  • Конструкторы

Edit

Спасибо за комментарии, теперь я могу привести соответствующую часть спецификации JVM. Здесь это подробная процедура инициализации.

Ответ 2

3 - это статический инициализатор, он запускается один раз, когда класс загружается, что происходит первым.

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

1 - будет выполняться при построении объекта после (3) и (2)..

Подробнее здесь

Ответ 3

Сначала выполняются статические блоки.

И затем блокировки экземпляра экземпляра

См. JLS для инсайдерских экземпляров.

{

//инструкция sop

}

у вас не может быть статута возврата в блоке инициализации экземпляра, как и конструкторы.

Ответ 4

Test(){System.out.println("1");}

    {System.out.println("2");}

    static{System.out.println("3");}

статические вещи выполняются первыми, {System.out.println("2");} не является частью функции, потому что его область действия вызывается первым, а Test(){System.out.println("1");} вызывается последним, потому что другие два называются первыми

Ответ 5

Сначала класс загружается в JVM и происходит инициализация класса. На этом этапе выполняются статические блоки. "{...}" - это просто синтаксический эквивалент "static {...}". Поскольку в коде уже есть "статический {...}" блок, к нему будет добавлен "...". Вот почему у вас есть 3 напечатанных до 2.

После того, как класс загружен, java.exe(который, как я предполагал, вы выполнили из командной строки), найдет и запустит основной метод. Основной статический метод инициализирует экземпляр, конструктор которого вызывается, поэтому вы получите последний "1".

Ответ 6

Поскольку код static{} запускается, когда класс сначала инициализируется в JVM (т.е. еще до main()), экземпляр {} вызывается, когда экземпляр сначала инициализируется, прежде чем он будет создан, и тогда конструктор вызывается после всего, что делается.

Ответ 7

Я получил код, похожий на байт-код, в ASM.

Я думаю, что это может ответить на ваш вопрос, объяснив, что произошло, когда объект создан по этому поводу.

public class Test {
static <clinit>() : void
GETSTATIC System.out : PrintStream
LDC "3"
INVOKEVIRTUAL PrintStream.println(String) : void
RETURN


<init>() : void
ALOAD 0: this
INVOKESPECIAL Object.<init>() : void
GETSTATIC System.out : PrintStream
LDC "2"
INVOKEVIRTUAL PrintStream.println(String) : void
GETSTATIC System.out : PrintStream
LDC "1"
INVOKEVIRTUAL PrintStream.println(String) : void
RETURN

public static main(String[]) : void
NEW Test
INVOKESPECIAL Test.<init>() : void
RETURN
}

мы видим, что LDC "3" находится в "clinit", это инициализатор класса.

Время жизни объекта обычно равно: loading class → linking class → инициализация класса → создание объекта → use → GC. Вот почему 3 появляется первым. И поскольку это находится на уровне класса, а не уровне объекта, он будет отображаться один раз, когда тип класса будет загружен один раз. Подробнее см. внутри виртуальной машины Java2: время жизни типа

LDC "2" и `LDC "1" находится в "init", конструкторе.

Причина, почему это в этом порядке: Constructor сначала выполнит некоторую команду implict, такую ​​как супер-конструктор и код в {} класса, а затем выполнит код, который в их ядре constorutor.

То, что компилятор сделает с java файлом.

Ответ 8

Полное объяснение

Порядок выполнения подобен,

  • статический блок
  • блок экземпляра
  • Конструктор

Объяснение

Статический блок всегда будет вызываться только один раз в самом начале всякий раз, когда к нему обращаются какие-либо средства, в том случае, когда вы запускаете программу. (Для этого подразумевается статический блок). Это не зависит от экземпляров, поэтому не вызывается снова при создании новых экземпляров.

Затем для каждого созданного экземпляра вызывается блок Instance initialization, и после этого создается конструктор для каждого экземпляра. Потому что оба они могут использоваться для создания экземпляра экземпляра.

Является ли блок инициализации экземпляра, фактически вызываемый перед конструктором?

После компиляции код станет,

public class Test {

  Test(){
    super();
    System.out.println("2");
    System.out.println("1");
  }


  static {
    System.out.println("3");
  }

  public static void main(String args[]) {
    new Test();
  }
}

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

Из этой документации

Компилятор Java копирует блоки инициализации в каждый конструктор. Поэтому этот подход может использоваться для совместного использования блока кода между несколькими конструкторами.

Ответ 9

Не похоже, что кто-то сказал, почему 3 печатается только один раз. Поэтому я бы добавил, что это связано с тем, почему оно напечатано первым.

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

Скопированный код, содержащий 2, добавляется к конструкции, как описано выше, потому что это своего рода предварительное условие для всех конструкторов в классе. Вы не знаете, что произойдет в конструкторах Test, но вам гарантировано, что все они начнутся с печати 2. Таким образом, это происходит прежде всего в любом конкретном конструкторе и вызывается каждый раз, когда вызывается конструктор (ny).