В каком порядке выполняются блоки инициализатора static/instance в 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");
}
}
и получил этот выход:
старт
static - grandparent
static - parent
статический - ребенок!--8--> пример-дедушка
конструктор-дедушка
экземпляр-родитель
конструктор - родитель
экземпляр - ребенок
конструктор - ребенок
Конец
очевидный ответ из этого заключается в том, что блоки родителей работают до своих детей, но это может быть просто совпадением и не помогает, если два класса не находятся в одной иерархии.
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");
}
}
как следует из имени класса, я никогда не ссылался на новый класс нигде. Новая программа дала тот же результат, что и старая.
7 ответов
статический инициализатор класса запускается при первом доступе к классу либо для создания экземпляра, либо для доступа к статическому методу или полю.
Итак, для нескольких классов это полностью зависит от кода, который запускается, чтобы заставить эти классы загружаться.
см. раздел 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), и выполняется оператор assert (§14.10), лексически вложенный в T (§8.1.3).
(и несколько предложений ласки)
ответы Кита и Криса оба отличные, я просто добавляю некоторые детали для моего конкретного вопроса.
статические блоки инициализации выполняются в том порядке, в котором их классы инициализируются. Итак, что это за порядок? Per JLS 12.4.1:
класс или тип интерфейса T будет инициализирован непосредственно перед первым появлением любого из следующих типов:
- T-это класс, и создается экземпляр T.
- T-класс и вызывается статический метод, объявленный T.
- назначается статическое поле, объявленное T.
- используется статическое поле, объявленное T, и поле не является постоянной переменной (§4.12.4).
- T-класс верхнего уровня, и выполняется оператор assert (§14.10), лексически вложенный в T.
вызов определенных отражающих методов в классе Class и в пакете java.ленг.reflect также вызывает класс или инициализация интерфейса. Класс или интерфейс не будут инициализированы ни при каких других обстоятельствах.
чтобы проиллюстрировать, вот пошаговое руководство того, что происходит в Примере:
- введите main
- печать "запустить"
- попытка создать первый экземпляр Child, который требует инициализации Child
- попытка инициализации дочернего элемента вызывает инициализацию Родитель!--4-->
- попытка инициализации родителя вызывает инициализацию бабушки и дедушки
- в начале инициализации дедушки и бабушки запускается статический блок инициализации дедушки и бабушки
- технически объект получает последнее слово в цепи инициализации в силу того, что он является родителем дедушки и бабушки, но ему нечего добавить
- после статического блока инициализации бабушки и дедушки заканчивается, программа возвращается к статическому блоку инициализации родителя
- после завершения статического блока инициализации родителя программа возвращается к статическому блоку инициализации ребенка
- В этот момент ребенок инициализируется, поэтому его конструктор может продолжить
- поскольку IAmAClassThatIsNeverUsed никогда не ссылается, ни один из его кода никогда не запускается, включая статические блоки инициализатора
- остальные это пошаговое руководство не касается статических инициализаторов и включено только для полноты
- конструктор ребенка неявно вызывает super () (т. е. конструктор родителя)
- конструктор родителя неявно вызывает super () (т. е. конструктор дедушки и бабушки)
- конструктор дедушки и бабушки делает то же самое, что не имеет никакого эффекта (опять же, объекту нечего вносить)
- сразу после вызова конструктора дедушки и бабушки в super () приходит Блок инициализатора экземпляра дедушки
- остальная часть конструктора конструктора дедушки и бабушки запускается, и конструктор завершается
- программа возвращается к родительскому конструктору сразу после вызова super () (т. е. конструктора дедушки и бабушки) разрешает
- как и выше, инициализатор экземпляра Parent делает свое дело, и его конструктор заканчивает
- аналогично, программа возвращается и завершает дочерние конструктор
- в этот момент объект был инстанцирован
- печать "END"
- завершить нормально
инициализация класса состоит из выполнения его статические инициализаторы и инициализаторы для статических полей (переменных класса), объявленного в классе.
инициализация интерфейса состоит из выполнения инициализаторов для полей (констант), объявленных в интерфейсе.
прежде, чем класс инициализируется, его прямой суперкласс должен быть инициализирован, но интерфейсы, реализованные классом, не инициализируются. Аналогично, суперинтерфейсы интерфейса не инициализируются до инициализации интерфейса.
вы можете иметь несколько статических и инициализаторы экземпляра в одном классе, поэтому
- статические инициализаторы вызываются в текстовом порядке, в котором они объявлены (от 12.4.2)
- инициализаторы экземпляра в текстовом порядке, в котором они объявлены (с 12.5)
каждый выполняется так, как если бы это был один блок.
http://docs.oracle.com/javase/tutorial/java/javaOO/initial.html
пожалуйста, проверьте документацию java.
тогда ясно указано, как бы статические блоки не были там, они будут выполняться как один блок в порядке, в котором они появляются
и
мое понимание здесь java смотрит ваш код как
static{
i=1;
i=2;
}
static int i;
вот почему вы получаете выход 2
Надежда это полезно
есть один случай, когда статический блок не будет вызываться.
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