Почему статические блоки и блоки инициализации экземпляров в Enums ведут себя не так, как в классах

При подготовке к сертификационному тесту Java я узнал, что статические блоки инициализации запускаются один раз при загрузке класса в порядке их появления в исходном коде, что блоки инициализации экземпляра запускаются каждый раз при создании экземпляра, а код в конструкторах запускается каждый раз. после этого создается экземпляр. Чтобы проверить это, я создал класс с некоторыми статическими блоками и блоками инициализации экземпляра и конструктором с материалами для печати. Все работало, как и ожидалось, за исключением того, что я думал, что «загружено» означает только во время выполнения, но я предполагаю, что это происходит, когда создается первый экземпляр, поскольку я вообще не получаю никакого вывода, если не создам хотя бы 1 экземпляр класса. Затем я попробовал то же самое с перечислением, и порядок был отключен. Во-первых, блоки инициализации запускаются один раз для каждого значения, которое имеет перечисление, когда перечисление впервые упоминается в коде, во-вторых, блоки инициализации, помеченные как статические, запускаются после того, что я предположил, были блоками инициализации экземпляра! Это противоположно тому, что я ожидал. Вот разбивка моих вопросов.

  1. Почему блоки инициализации, помеченные как статические, выполняются последними в перечислении?
  2. Может ли перечисление иметь блоки инициализации экземпляра?
  3. Почему блоки, которые я считал блоками инициализации экземпляра, запускаются только один раз при загрузке перечисления, а не каждый раз при ссылке на новое значение перечисления?
  4. Статические блоки инициализации класса запускаются, когда класс "загружается". Что значит загружен? Это происходит только один раз, когда объект создается в классе?

Спасибо! Это очень сбивает меня с толку.

public class EnumInit {
public static void main(String[] args) {
    System.out.println(Color.RED.toString() + " Main");
    MyInit myInit = new MyInit();
    System.out.println(Color.BLUE.toString() + " Main");
    MyInit mySecondInit = new MyInit();

}
}

enum Color {    
RED, BLUE, GREEN;
String instanceVar = "Enum Instance Variable Text";
static { System.out.println("Enum Static init block 1"); }
{ System.out.println("Enum Instance init block 1"); }
static { System.out.println("Enum Static static init block 2"); }
Color() { 
    System.out.println(instanceVar);
    System.out.println("Enum String Literal"); 
}
{ System.out.println("Enum Instance init block 2"); }   
}

class MyInit {
String instanceVar = "Class Instance Variable Text";
static { System.out.println("Class Static init block 1"); }
{ System.out.println("Class Instance init block 1"); }
static { System.out.println("Class Static static init block 2"); }
MyInit() { 
    System.out.println(instanceVar);
    System.out.println("Class String Literal"); 
}
{ System.out.println("Class Instance init block 2"); }  
}

person paniclater    schedule 16.12.2013    source источник
comment
для № 4: классы не загружаются, пока они не будут использованы по назначению. Это может быть вызов статического метода класса, создание экземпляра или вызов Class.forName() или что-то еще. Я считаю, что это сделано для того, чтобы свести к минимуму количество классов, загруженных в настоящее время в постоянное поколение.   -  person Mark W    schedule 17.12.2013


Ответы (1)


В Java Language Specification сказано следующее: о enum константах

В дополнение к членам, которые тип перечисления E наследует от Enum, для каждой объявленной константы перечисления с именем n тип перечисления имеет неявно объявленное общедоступное статическое конечное поле с именем n типа E. Эти поля считаются объявлены в том же порядке, что и соответствующие константы перечисления, перед любыми статическими полями, явно объявленными в типе перечисления. Каждое такое поле инициализируется соответствующей ему константой перечисления.

So

enum Color {    
    RED, BLUE, GREEN;
    ...
}

на самом деле

public static final Color RED = new Color();
public static final Color BLUE = new Color();
public static final Color GREEN = new Color();

который будет оцениваться перед static блоками, которые у вас есть.

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

См. выше.

Может ли перечисление иметь блоки инициализации экземпляра?

Да, скомпилируй свой код и увидишь.

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

Константы перечисления создаются (создаются экземпляры) после инициализации типа перечисления. Вы не создаете новый enum в любое время

Color color = Color.RED;

Вы просто ссылаетесь на уже созданный существующий объект.

Статические блоки инициализации класса запускаются, когда класс "загружается". Что значит загружен? Это происходит только один раз, когда объект создается в классе?

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

person Sotirios Delimanolis    schedule 16.12.2013
comment
Итак, запускаются ли блоки инициализации, не помеченные как статические, при запуске каждого неявного конструктора, а статические блоки запускаются последними? - person paniclater; 17.12.2013
comment
@paniclater Да, эти блоки инициализатора экземпляра запускаются, когда new Color(); вызывается неявно. Поскольку константы enum — это первое, что вы объявляете в теле enum, они всегда будут происходить перед любыми блоками static, объявленными в enum. - person Sotirios Delimanolis; 17.12.2013
comment
Спасибо, это действительно делает этот процесс действительно понятным. - person paniclater; 17.12.2013
comment
@paniclater Не за что. Рассмотрите возможность принятия ответа, если что-то не ясно. - person Sotirios Delimanolis; 17.12.2013