前言

上一篇我们知道了一个类的生命周期是:加载->验证->准备->解析->初始化->使用->卸载。
当初始化完成以后,一个类所有的类变量(被static修饰的变量)都被赋值。但是未被static修饰的成员变量又是何时被赋值的呢?

一个类何时会被初始化

一个类何时被初始化可以分为以下几类:

1.创建类的实例(new)。
 
2.访问某个类或接口的静态变量,或者对该静态变量赋值。

3.调用类的静态方法。

4.通过反射方式执行以上三种行为。

5.初始化子类的时候,会触发父类的初始化。

6.Java虚拟机启动时被标明为启动类的类。(有main方法的类)

7.JDK 1.7开始提供的动态语言支持。(了解即可)

我们来说道说道第3点和第6点

我们平常在使用main方法和调用某个类的静态方法的时候,是不是发现,并不能直接调用静态方法和main方法所在类的非静态方法和非静态变量。

可是明明不是说了在调用静态方法和执行main方法的时候,所在的类已经被初始化了吗?

是的!在上一章节我们就说,类初始化的时候会按照我们编写代码的顺序为类变量(static修饰的变量)进行赋值。注意哦,此时这个类仅仅只有静态变量被正确赋值了哦。

public class People{
    private static String name ="lisi";
    private int age = 18;
    
    static{
        name ="zhangsan";
    }
}

如上述代码,在该类被初始化之后,首先按照代码顺序将类变量(被static修饰的变量)赋值。所以name被赋值”lishi”,然后再将name赋值为 “zhangsan”。此时age并没有值,直接直接调用会报错。

一个类何时被实例化

上一个章节中,我们明白了加载->验证->准备->解析->初始化的具体细节。

当初始化完成后,一个类的静态变量被正确赋值。如果这个对象是被new出来的。那么在初始化完成之后会进入实例化阶段。

实例化的具体步骤为:

父类非静态成员初始化语句(包括代码块,按照在类定义中的顺序执行)->父类构造函数->子类非静态成员初始化语句(包括代码块,按照在类定义中的顺序执行)->子类构造方法()

注意哦!
如果这个类有父类不光是先实例化父类。整体流程如下:
1. 加载父类
    1.1 为静态属性分配存储空间并赋初始值 
    1.2 执行静态初始化块和静态初始化语句(从上至下)
2. 加载子类
    2.1 为静态属性分配存储空间
2.2 执行静态初始化块和静态初始化语句(从上至下)
    3. 加载父类构造器
3.1 为实例属性分配存数空间并赋初始值 
    3.2 执行实例初始化块和实例初始化语句
    3.3 执行构造器内容
4. 加载子类构造器
    4.1 为实例属性分配存数空间并赋初始值 
    4.2 执行实例初始化块和实例初始化语句
    4.3 执行构造器内容

实例化的内存模型

到这里,我们就非常的清楚为什么在静态方法中不能直接调用非静态的变量,因为此时的非静态变量并没有被赋值。

以前我在学习的时候,new一个对象出来大家总是在说,我们初始化了这个对象,然后balabala。。。

导致我对于初始化和实例化里的静态变量和非静态变量何时被赋值一直有点模糊。new一个对象出来准确的来说是进行了初始化和实例化两个步骤,这样应该就会清晰了很多。

初始化完成以后,类被存放在方法区,注意哦,此时并没有存放在堆内存中。

只有当对象实例化进入堆内存中以后才会对非静态变量进行初始化赋值。

总结

这下总算吧一个类的初始化和实例化的细节搞明白了,关于文中的方法区,堆内存等内容,下一节再具体分析。

版权声明:本文为zhxiansheng原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/zhxiansheng/p/11160275.html