在 class 级别初始化 属性 将在加载或构造函数期间分配内存?

Initialize the property at class level will allocate memory during load or constructor?

我们有 class 比方说 AnimalShelter,它有一个动物列表。当我们在 class 级别初始化它时,它会在加载 class 期间或在创建对象期间创建内存。

class Animal{
  String name;
}

class AnimalShelter{
  private List<Animal> animals = new ArrayList<>();
}

我的理解是它会在内存加载期间初始化 属性 或者当我们构造一个对象时它会创建一个带有新列表的对象。一个初始化总是在 class 级别。

如果我们在构造函数中初始化列表,它只有一个引用,当我们创建一个对象时会分配内存。 class 级别

没有内存分配
class AnimalShelter{
  private List<Animal> animals; 
  AnimalShelter(){
    animals  = new ArrayList<>();
  }
}

我们如何验证这个东西或者我对这个理解完全错误?

根据JLS 16第12.5节创建新Class实例,创建实例后,顺序为:

  1. 将构造函数的参数分配给新创建的参数变量 对于此构造函数调用。
  2. 如果此构造函数以显式构造函数调用开始 同一个 class 中的另一个构造函数(使用 this),然后计算参数 并使用这五个递归地处理构造函数调用 脚步。如果该构造函数调用突然完成,则此过程 出于同样的原因突然完成;否则,继续第 5 步。
  3. 此构造函数不是以显式构造函数调用开始的 同一个 class 中的另一个构造函数(使用 this)。如果此构造函数用于 class 而非 Object,则此构造函数将以显式开头 或隐式调用 superclass 构造函数(使用 super)。评估 递归使用 superclass 构造函数调用的参数和过程 同样的五个步骤。如果该构造函数调用突然完成,则 出于同样的原因,此过程突然完成。否则,继续 与步骤 4.
  4. 为此class执行实例初始化器和实例变量初始化器, 将实例变量初始化器的值分配给相应的 实例变量,按照它们在文本中出现的从左到右的顺序 class 的源代码。如果执行这些初始化程序中的任何一个结果 在异常中,则不会处理进一步的初始值设定项,并且此过程 以相同的异常突然完成。否则,继续第 5 步。
  5. 执行此构造函数主体的其余部分。如果该执行完成 突然,然后这个过程出于同样的原因突然完成。 否则,此过程正常完成。

例如,下面将依次打印A、B、C、D、E。请注意,虽然 C 块在源代码中 after 构造函数,但它在 before 执行,因为实例初始化器都被执行 构造函数之前。

public class Test {
    {
        System.out.println("A");
    }
    
    Sub sub = new Sub();
    
    public Test() {
        System.out.println("D");
    }
        
    {
        System.out.println("C");
    }
    
    public static void main(String[] args) {
        var test = new Test();
        System.out.println("E");
    }
}

class Sub {
    public Sub() {
        System.out.println("B");
    }
}