Вопрос-ответ

What is an initialization block?

Что такое блок инициализации?

Мы можем поместить код в конструктор, метод или блок инициализации. Для чего нужен блок инициализации? Обязательно ли, чтобы он был в каждой программе Java?

Переведено автоматически
Ответ 1

Прежде всего, существует два типа блоков инициализации:


  • блоки инициализации экземпляра, и

  • статические блоки инициализации.

Этот код должен иллюстрировать их использование и в каком порядке они выполняются:

public class Test {

static int staticVariable;
int nonStaticVariable;

// Static initialization block:
// Runs once (when the class is initialized)
static {
System.out.println("Static initalization.");
staticVariable = 5;
}

// Instance initialization block:
// Runs each time you instantiate an object
{
System.out.println("Instance initialization.");
nonStaticVariable = 7;
}

public Test() {
System.out.println("Constructor.");
}

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

С принтами:

Static initalization.
Instance initialization.
Constructor.
Instance initialization.
Constructor.

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

Ответ 2

хотел бы добавить к ответу @aioobe

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


  1. статические блоки инициализации суперклассов


  2. статические блоки инициализации класса


  3. блоки инициализации экземпляров суперклассов


  4. конструкторы суперклассов


  5. блоки инициализации экземпляра класса


  6. конструктор класса.


Следует иметь в виду пару дополнительных моментов (пункт 1 является повторением ответа @aioobe):


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


  2. Блок инициализации экземпляра фактически копируется компилятором Java в каждый конструктор, который есть в классе. Таким образом, каждый раз код в блоке инициализации экземпляра выполняется точно перед кодом в конструкторе.


Ответ 3

хороший ответ от aioobe, добавляющий еще несколько моментов

public class StaticTest extends parent {
static {
System.out.println("inside satic block");
}

StaticTest() {
System.out.println("inside constructor of child");
}

{
System.out.println("inside initialization block");
}

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

class parent {
static {
System.out.println("inside parent Static block");
}
{
System.out.println("inside parent initialisation block");
}

parent() {
System.out.println("inside parent constructor");
}
}

это дает

inside parent Static block
inside satic block
inside parent initialisation block
inside parent constructor
inside initialization block
inside constructor of child
inside parent initialisation block
inside parent constructor
inside initialization block
inside constructor of child
inside main

это похоже на констатацию очевидного, но кажется немного более понятным.

Ответ 4

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

package test;

class A {
A() {
print();
}

void print() {
System.out.println("A");
}
}

class B extends A {
static int staticVariable2 = 123456;
static int staticVariable;

static
{
System.out.println(staticVariable2);
System.out.println("Static Initialization block");
staticVariable = Math.round(3.5f);
}

int instanceVariable;

{
System.out.println("Initialization block");
instanceVariable = Math.round(3.5f);
staticVariable = Math.round(3.5f);
}

B() {
System.out.println("Constructor");
}

public static void main(String[] args) {
A a = new B();
a.print();
System.out.println("main");
}

void print() {
System.out.println(instanceVariable);
}

static void somethingElse() {
System.out.println("Static method");
}
}

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

Во-первых, они называются переменными класса, они принадлежат классу, а не конкретному экземпляру класса. Все экземпляры класса совместно используют эту статическую переменную (class). Каждая переменная имеет значение по умолчанию, в зависимости от примитивного или ссылочного типа. Другое дело, когда вы переназначаете статическую переменную в некоторых членах класса (блоках инициализации, конструкторах, методах, свойствах) и при этом вы изменяете значение статической переменной не для конкретного экземпляра, вы изменяете его для всех экземпляров. В заключение статической части я скажу, что статические переменные класса создаются не при первом создании экземпляра класса, они создаются при определении вашего класса, они существуют в JVM без необходимости в каких-либо экземплярах. Для правильного доступа к статическим элементам из внешнего класса (класса, в котором они не определены) используется имя класса, за которым следует точка, а затем статический элемент, к которому вы хотите получить доступ (шаблон: <CLASS_NAME>.<STATIC_VARIABLE_NAME>).

Теперь давайте посмотрим на приведенный выше код:

Точка входа - это основной метод - всего три строки кода. Я хочу сослаться на пример, который в настоящее время одобрен. Согласно этому первое, что должно быть напечатано после печати "Блока статической инициализации", - это "Блок инициализации", и вот мое несогласие, блок нестатической инициализации не вызывается перед конструктором, он вызывается перед любыми инициализациями конструкторов класса, в котором определен блок инициализации. Конструктор класса - это первое, что задействовано при создании объекта (экземпляра класса), а затем, когда вы вводите конструктор, первая вызываемая часть является либо неявным (по умолчанию) суперконструктором, либо явным суперконструктором, либо явным вызовом другого перегруженного конструктора (но в какой-то момент, если есть цепочка перегруженных конструкторов, последний вызывает суперконструктор, неявно или явно).

Происходит полиморфное создание объекта, но перед входом в класс B и его основной метод JVM инициализирует все переменные класса (статические), затем проходит через блоки статической инициализации, если таковые существуют, а затем входит в класс B и начинает выполнение основного метода. Он переходит к конструктору класса B, затем немедленно (неявно) вызывает конструктор класса A, используя полиморфизм метод (переопределенный метод), вызываемый в теле конструктора класса A, является тем, который определен в классе B, и в этом случае переменная с именем instanceVariable используется перед повторной инициализацией. После закрытия конструктора класса B поток возвращается к конструктору класса B, но сначала он переходит к блоку нестатической инициализации, прежде чем печатать "Конструктор". Для лучшего понимания отладки с помощью какой-либо IDE я предпочитаю Eclipse.

java