Разница в порядке статической инициализации в зависимости от основного класса в иерархии наследования JavaJAVA

Программисты JAVA общаются здесь
Ответить
Anonymous
 Разница в порядке статической инициализации в зависимости от основного класса в иерархии наследования Java

Сообщение Anonymous »

Я пытаюсь понять загрузку классов Java и порядок статической инициализации в иерархии наследования, но получаю разное поведение в зависимости от того, какой класс содержит метод main.
У меня есть два очень похожих случая, когда единственное различие заключается в расположении метода main (либо в A1, либо в A4). Структура класса в обоих случаях одинакова.
Первый код:

Код: Выделить всё

package javatest;

// A1.java

import java.io.Serializable;

class A1 {

static {
new A2(new A1());
System.out.println("A1-S");
}

{
System.out.println("A1-N1");
}

private A1 a1;

public A1() {
System.out.println("A1()");
}

public A1(A1 a1) {
this();
System.out.println("A1(A1)");
this.a1 = a1;
new A2(a1);
}

public void metoda1() {
new A1();
System.out.println("A1.metoda1()");
}

{
System.out.println("A1-N2");
}

public static void main(String[] args) {

}

}

class A2 extends A1 implements Serializable {

static {
System.out.println("A2-S");
}

{
System.out.println("A2-N");
}

protected A2() {
System.out.println("A2()");
this.metoda1();
}

public A2(A1 a1) {
System.out.println("A2(A1)");
a1.metoda1();
}

@Override
public void metoda1() {
super.metoda1();
System.out.println("A2.metoda1()");
}

public void metoda2() {
System.out.println("A2.metoda2()");
}
}

class A3 extends A2 {

A2 a2 = null;

static {
System.out.println("a3-S");
}

{
System.out.println("a3-N1");
}

public A3() {
super();
System.out.println("a3()");
}

public A3(A2 a2) {
this();
this.a2 = a2;
System.out.println("a3(A2)");
}

{
System.out.println("a3-N2");
}

public A3(A1 a1, A2 a2) {
this(a2);
System.out.println("a3(A1,A2)");
}

public void metoda2() {
System.out.println("a3.metoda()");
}
}

class A4 extends A3 {

A1 a1 = new A1();
A3 a2 = new A3(new A1(new A1()),  new A2(a1));
Serializable a3 = new A3();

static {
System.out.println("A4-S");
}

public A4() {
super();
System.out.println("A4()");
super.metoda1();
}

{
System.out.println("A4-N");
}

}

class A5 extends A1 {
static {
System.out.println("A5-S");
}

public A5() {
super();
System.out.println("A5()");
}

{
System.out.println("A5-N");
}
}
Второй код:

Код: Выделить всё

package javatest;

// A1.java

import java.io.Serializable;

class A1 {

static {
new A2(new A1());
System.out.println("A1-S");
}

{
System.out.println("A1-N1");
}

private A1 a1;

public A1() {
System.out.println("A1()");
}

public A1(A1 a1) {
this();
System.out.println("A1(A1)");
this.a1 = a1;
new A2(a1);
}

public void metoda1() {
new A1();
System.out.println("A1.metoda1()");
}

{
System.out.println("A1-N2");
}

}

class A2 extends A1 implements Serializable {

static {
System.out.println("A2-S");
}

{
System.out.println("A2-N");
}

protected A2() {
System.out.println("A2()");
this.metoda1();
}

public A2(A1 a1) {
System.out.println("A2(A1)");
a1.metoda1();
}

@Override
public void metoda1() {
super.metoda1();
System.out.println("A2.metoda1()");
}

public void metoda2() {
System.out.println("A2.metoda2()");
}
}

class A3 extends A2 {

A2 a2 = null;

static {
System.out.println("a3-S");
}

{
System.out.println("a3-N1");
}

public A3() {
super();
System.out.println("a3()");
}

public A3(A2 a2) {
this();
this.a2 = a2;
System.out.println("a3(A2)");
}

{
System.out.println("a3-N2");
}

public A3(A1 a1, A2 a2) {
this(a2);
System.out.println("a3(A1,A2)");
}

public void metoda2() {
System.out.println("a3.metoda()");
}
}

class A4 extends A3 {

A1 a1 = new A1();
A3 a2 = new A3(new A1(new A1()), new A2(a1));
Serializable a3 = new A3();

static {
System.out.println("A4-S");
}

public A4() {
super();
System.out.println("A4()");
super.metoda1();
}

{
System.out.println("A4-N");
}

public static void main(String[] args) {

}

}

class A5 extends A1 {
static {
System.out.println("A5-S");
}

public A5() {
super();
System.out.println("A5()");
}

{
System.out.println("A5-N");
}
}
Вывод «Первого кода»:
A2-S

A1-N1

A1-N2

A1()

A1-N1

A1-N2

A1()

A2-N

A2(A1)

A1-N1

A1-N2

A1()

A1.metoda1()

A1-S
Вывод «Второго кода»:
A1-N1

A1-N2

A1()

A1-N1

A1-N2

A1()

A2-N

A2(A1)

A1-N1

A1-N2

A1()

A1.metoda1()

A1-S

A2-S

a3-S

A4-S
Я понимаю все основные концепции, например тот факт, что статический блок инициализации выполняется только один раз при загрузке класса, но такое поведение немного сбивает с толку.
Мои рассуждения следующие:
Когда в коде 2 метод main находится внутри класса A4, JVM начинается с A4 и может «видеть» всю иерархию наследования: A4 расширяет A3, A3 расширяет A2, а A2 расширяет A1. По этой причине я предположил, что во время выполнения статического блока инициализации в A1, когда он встречает выражение new A2(new A1()), сначала оценивается внутреннее выражение new A1(), поскольку JVM уже знает об A2 из-за иерархии классов, даже если A2 еще не полностью инициализирован.
В коде 1, где основной метод находится внутри класса A1, JVM изначально «видит» только A1. В начале нет активного обхода иерархии наследования. Он входит в статический блок A1, но внутри этого статического блока у нас есть новый A2(new A1()), поэтому JVM впервые встречает A2. Поскольку он еще не знает или не инициализировал A2, он приостанавливает выполнение статического блока A1, сначала переходит к инициализации A2, а затем продолжает.
Поправьте меня, если я ошибаюсь.
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «JAVA»