一、一个类及对象初始化的过程

什么时候需要初始化一个类

首次创建某个对象时

Dog dog = new Dog();

或是首次访问某个类的静态方法或者是静态字段的时候

Dog.staticFields;

获得类的资源

然后JVM就会载入Dog.class,生成一个class对象。这个时候如果有静态的方法或者变量,静态初始化的动作都会被执行。静态初始化在程序运行过程中只会在Class对象首次加载的时候运行一次,这些资源都会放在JVM的方法区

方法区又叫静态区,和堆一样,被所有的线程共享。

方法区包含的都是在整个程序中永远唯一的元素,包含所有的class和static变量

初始化对象Dog dog = new Dog()

  • 第一次创建Dog对象先执行上面的一二步
  • 在堆上为Dog对象分配足够的存储空间,所有属性和方法都被设置成默认值(数字为0,字符为null,布尔为false,而所有引用被设置成null)
  • 执行构造函数时检查是否有父类,如果有父类就会先调用父类的构造函数,这里假设Dog没有父类,执行默认值字段的赋值即方法的初始化动作
  • 执行构造函数

有父类情况下的初始化

假设Dog extends Animal

  1. 执行第一步,找出Dog.class文件,接着在加载过程中会发现他有一个基类,于是先执行Animal类的第一二步,加载其静态方法和变量,加载结束之后再加载子类Dog的静态方法和变量。

    如果Animal类还有父类就继续类推,最终的基类叫做根基类

    因为子类的static初始化可能会依赖父类的静态资源,所以要先加载父类的静态资源。

  2. 接着new Dog对象,先为Dog对象分配存储空间->到Dog的构造函数->创建默认的属性。这里其构造函数中有一个隐含的super(),即父类构造函数,所以这时候会跳转到父类Animal的构造函数。

    Java会帮我们完成构造函数的补充,Dog实际隐式的构造函数如下

    Dog(){
    //创建默认的属性和方法
    //调用父类的构造函数super()(可显式写出)
    //对默认属性和方法分别进行赋值和初始化
    }
  3. 父类Animal执行构造函数之前也是先分配存储空间->到其的构造函数->创建默认的属性,接着发现自己已经没有父类了,这时就给它的默认的属性赋值和方法的初始化

  4. 接着执行构造函数余下的部分,结束后跳转到子类Dog的构造函数。

  5. 子类Dog对默认属性和方法分别进行赋值和初始化,接着完成构造函数接下来的部分

为什么要执行父类的构造方法后才能继续子类的属性及方法赋值?

因为子类的非静态变量和方法的初始化有可能使用到其父类的属性或方法,所以子类构造默认的属性的方法之后不应该进行赋值,而要跳转到父类的构造方法完成父类对象的构造之后,才来队自己的属性和方法进行初始化。

因此子类的构造函数显示调用父类构造函数super()时要强制写在第一行的原因,程序需要跳转到父类构造函数完成父类对象的构造之后才能执行子类构造函数的余下部分。

为什么对属性和方法初始化之后再执行构造函数的其他部分?

因为构造函数中的显式部分有可能使用到对象的属性和方法。

初始化块的执行顺序

首先static初始化块是在构造函数之前执行的,而且只执行一次,即类首次加载时。

初始化是在类加载的时候完成的,而构造方法是生成对象的,是在类加载之后完成的,所以先执行初始化块

初始化块分为静态和非静态,静态初始化块是执行一次,无论new多少次,它只会执行一次。

而非静态初始化块则不是这样,每次new都会执行,而执行顺序是先静态后非静态。

无论静态或者是非静态,它们的执行顺序都在构造方法之前执行

在你在父类方法的情况下:在执行完非静态块后立即执行其构造函数然后执行子类的非静态代码块然后执行子类的构造方法。

二、类实例化

类实例化的时机

一般是使用new和反射的时候

类实例化的过程

  1. 前提:.class文件已被加载完毕(经过加载、解析和初始化)

  2. 系统赋初值:在堆内存中为xxx类及其父类中的非静态成员变量开辟空间,并赋系统默认初值(0,null,false),因为成员变量有初值,函数才能使用。

    即使子类覆盖了父类成员变量,仍会给父类所有成员变量分配空间,此时子类中多了一个属性,分态性,空间储存,分别存储父类和子类的成员变量。

  3. 父类成员变量的显式初始化和构造代码块的初始化

    两者的执行顺序就是代码排列顺序(和静态变量显示初始化和静态构造代码块初始化一样),但和前面静态一样,当构造代码块在前二成员变量声明在后时,构造代码块中只能赋值,不能访问。另外实际上两者都会被自动装入父类构造函数的最前面

  4. 父类构造函数执行

    Java强制要求Object对象之外的所有类的构造函数的第一条语句必须是超类构造函数的调用语句,以保证所创建实例的完整性。

  5. 成员变量的显式初始化和子类构造代码块的初始化

  6. 构造函数初始化

    每一个Java中的对象都至少会有一个构造函数,如果没有显式定义构造函数,那么JVM会自动生成一个默认无参的构造函数。在编译生成的字节码中,这些构造函数会被命名为()方法。参数列表与Java语言书写的构造函数的参数列表相同。

类的加载机制和实例化的区别

类的加载机制 类的实例化
是什么 把类从.class文件加载到虚拟机,为实例化做准备 创建一个类的实例(对象)的过程
实例化的对象 只有类变量按照程序代码被赋值(()方法的执行) 非静态变量的初始化,以及构造方法的执行(<init()方法的执行)
执行次数 一个类只会执行1次 一个类可执行多次