В каком порядке выполняются блоки инициализатора статического/экземпляра в Java?
Говорите, что проект содержит несколько классов, каждый из которых имеет статический блок инициализатора. В каком порядке выполняются эти блоки? Я знаю, что в классе такие блоки выполняются в том порядке, в котором они появляются в коде. Я читал, что он одинаковый по классам, но какой-то пример кода, который я написал, не согласуется с этим. Я использовал этот код:
package pkg;
public class LoadTest {
public static void main(String[] args) {
System.out.println("START");
new Child();
System.out.println("END");
}
}
class Parent extends Grandparent {
// Instance init block
{
System.out.println("instance - parent");
}
// Constructor
public Parent() {
System.out.println("constructor - parent");
}
// Static init block
static {
System.out.println("static - parent");
}
}
class Grandparent {
// Static init block
static {
System.out.println("static - grandparent");
}
// Instance init block
{
System.out.println("instance - grandparent");
}
// Constructor
public Grandparent() {
System.out.println("constructor - grandparent");
}
}
class Child extends Parent {
// Constructor
public Child() {
System.out.println("constructor - child");
}
// Static init block
static {
System.out.println("static - child");
}
// Instance init block
{
System.out.println("instance - child");
}
}
и получил этот вывод:
START
статичный - бабушка и дедушка
static - parent
static - child
пример - grandparent
конструктор - grandparent
экземпляр - родительский конструктор - родительский
экземпляр - ребенок
конструктор - ребенок
END
Очевидный ответ на этот вопрос состоит в том, что блоки родителей выполняются перед их дочерними элементами, но это может быть просто совпадением и не помогает, если два класса не находятся в одной иерархии.
EDIT:
Я изменил свой пример кода, добавив его в LoadTest.java:
class IAmAClassThatIsNeverUsed {
// Constructor
public IAmAClassThatIsNeverUsed() {
System.out.println("constructor - IAACTINU");
}
// Instance init block
{
System.out.println("instance - IAACTINU");
}
// Static init block
static {
System.out.println("static - IAACTINU");
}
}
Как подразумевается именем класса, я никогда не ссылался на новый класс нигде. Новая программа произвела тот же результат, что и старый.
Ответы
Ответ 1
Статический инициализатор для класса запускается при первом доступе к классу, либо для создания экземпляра, либо для доступа к статическому методу или полю.
Итак, для нескольких классов это полностью зависит от кода, который запускается, чтобы заставить эти классы загружаться.
Ответ 2
См. разделы 12.4 и 12.5 JLS версии 8, они подробно рассказывают обо всем этом (12.4 для статических и 12.5, например переменных).
Для статической инициализации (раздел 12.4):
Класс или тип интерфейса T будет инициализирован непосредственно перед первым вхождением любого из следующих значений:
- T - это класс и создается экземпляр T.
- T - класс, и статический метод, объявленный T, вызывается.
- Назначено статическое поле, объявленное T.
- Используется статическое поле, объявленное T, и поле не является постоянной переменной (§4.12.4).
- T - класс верхнего уровня (§7.6), и выполняется утверждение (§14.10), лексически вложенное в T (§8.1.3).
(и несколько предложений кластера)
Ответ 3
Ответы Кита и Криса оба хороши, я просто добавляю некоторые подробности для моего конкретного вопроса.
Статические блоки инициализации выполняются в порядке, в котором инициализируются их классы. Итак, что это за заказ? Per JLS 12.4.1:
Класс или тип интерфейса T будет инициализирован непосредственно перед первым вхождением любого из следующих значений:
- T - это класс и создается экземпляр T.
- T - класс, и статический метод, объявленный T, вызывается.
- Назначено статическое поле, объявленное T.
- Используется статическое поле, объявленное T, и поле не является постоянной переменной (§4.12.4).
- T - класс верхнего уровня, и выполняется инструкция assert (§14.10), лексически вложенная в T.
Вызов некоторых отражающих методов в классе Class и в пакете java.lang.reflect также вызывает инициализацию класса или интерфейса. Класс или интерфейс не будут инициализированы ни при каких других обстоятельствах.
Чтобы проиллюстрировать это, прохождение игры, которое происходит в примере:
- Введите главное
- Печать "СТАРТ"
- Попытка создать первый экземпляр Child, для которого требуется инициализация Child
- Попытка инициализации Child вызывает инициализацию родителя
- Попытка инициализировать родительские причины инициализации бабушки и дедушки
- В начале инициализации бабушки и дедушки запускается статический блок инициализации бабушки дедушки
- Технически, объект получает последнее слово в цепочке инициализации благодаря тому, что он является родителем дедушки, но ему нечего вносить
- После завершения статической инициализации блока grandparent программа возвращается в родительский статический блок инициализации
- После завершения статической инициализации родительского пакета программа возвращается к блоку инициализации дочерних элементов
- В этот момент Child инициализируется, поэтому его конструктор может продолжить
- Поскольку IAmAClassThatIsNeverUsed никогда не ссылается, ни один из его кодов никогда не запускается, включая блоки статического инициализатора
- Остальная часть этого пошагового руководства не касается статических инициализаторов и включена только для полноты
- Конструктор Child неявно вызывает super() (т.е. конструктор родителя)
- Родительский конструктор неявно вызывает super() (т.е. конструктор grandparent)
- Конструктор grandparent делает то же самое, что не имеет никакого эффекта (опять же, Object ничего не может внести)
- Сразу же после вызова конструктора grandparent на super() появляется блок инициализатора экземпляра grandparent.
- Остальная часть конструктора конструктора grandparent запускается, и конструктор завершает
- Программа возвращается к родительскому конструктору сразу после вызова супер() (т.е. конструктора дедушки и бабушки) разрешает
- Как и выше, инициализатор родительского экземпляра делает свою вещь, а его конструктор заканчивается
- Аналогично, программа возвращает и завершает конструктор Child
- В этот момент объект был создан
- Печать "END"
- Обычно завершается
Ответ 4
Инициализация класса состоит в выполнении его статических инициализаторов и инициализаторов для статических полей (переменных класса), объявленных в классе.
Инициализация интерфейса состоит в выполнении инициализаторов для полей (констант), объявленных в интерфейсе.
Прежде чем инициализировать класс, его прямой суперкласс должен быть инициализирован, но интерфейсы, реализованные классом, не инициализируются. Аналогично, суперинтерфейсы интерфейса не инициализируются до инициализации интерфейса.
Ответ 5
У вас может быть несколько статичных и инициализаторов экземпляров в одном классе, поэтому
- Статические инициализаторы вызываются в текстовом порядке, который они объявляются (из 12.4.2)
- Инициализаторы экземпляров вызываются в текстовом порядке, который они объявляются (из 12.5)
Каждый выполняется, как если бы он был одним блоком.
Ответ 6
http://docs.oracle.com/javase/tutorial/java/javaOO/initial.html
проверьте документацию java.
тогда ясно сказано, как бы там ни были статические блоки, они будут выполняться как один блок в том порядке, в котором они появляются.
Итак,
Мое понимание здесь: java смотрит ваш код как
static{
i=1;
i=2;
}
static int i;
поэтому вы получаете вывод 2
надеюсь, что это полезно
Ответ 7
Существует один случай, когда статический блок не будет вызываться.
class Super {
public static int i=10;
}
class Sub extends Super {
static {
system.out.println("Static block called");
}
}
class Test {
public static void main (String [] args) {
system.out.println(Sub.i);
}
}
Вышеприведенный код выводит 10
Ответ 8
class A {
public A() {
// 2
}
}
class B extends A{
static char x = 'x'; // 0
char y = 'y'; // 3
public B() {
// 4
}
public static void main(String[] args) {
new B(); // 1
}
}
Числа в комментарии указывают порядок оценки, чем меньше, тем раньше.
Как показал пример,
- статическая переменная
- главный
- конструктор суперкласса
- переменная экземпляра
- конструктор