Java初始化块,静态初始化块,构造函数执行顺序及其变量存放位置

一. Java类的初始化和实例化

1. 类的初始化

完成程序执行前的准备工作。在这个阶段,静态的(变量,方法,代码块)会被执行。同时会在开辟一块存储空间用来存放静态的数据。初始化只在类加载的时候执行一次。

2. 类的实例化(实例化对象)

创建一个对象的过程。在这个过程中会在堆中开辟内存,将一些非静态的方法,变量存放在里面。在程序执行的过程中,可以创建多个对象,既多次实例化。每次实例化都会开辟一块新的内存。(就是调用构造函数)

在每个类初始化使用前,都会先对该类进行加载(不是指类的实例化)

类加载有几个步骤,加载 --》验证 --》准备 --》解析 --》初始化

在编译过程会把常量的值放入类的常量池中,在准备过程会对类变量(static修饰的变量)赋初始值,也就是零值,同时会将常量的值赋予常量;在初始化过程会按照类文件中的声明顺序执行类变量的赋值和静态语句块(static{}块),如果父类还没有初始化会先进行父类的初始化,完成后才会进行子类的初始化。
可以看到在初始化阶段就会执行static{}块的语句,而每一个类在运行过程中一般只会被加载一次(不是指实例化,特殊情况是使用其他类加载器对类进行加载),所以只会完成一次初始化过程,因此也就只会执行static{}块一次。
类的实例化和类的加载是两个不同的概念。

3. 类的初始化和实例化区别

执行次数触发机制是否执行构造方法生命周期执行内容
初始化(类级别)1Class.forName(),new,main方法的类,通过子类加载父类静态成员导致父类的初始化第五步为静态成员赋值,执行静态代码块
实例化(实例对象)Nnew,Class.newInstance()第六步在堆区分配内存空间,执行实例对象初始化,设置引用变量a指向刚分配的内存地址

二. Java静态初始化和非静态初始化

1. Java中初始化

在面向对象编程中,Java和C++不同之处在于,Java不仅有构造函数,还有一个"初始化块"(intialization block)的概念。Java中的初始化块在创建Java对象的隐式执行,并且在构造函数之前执行。

2. 静态初始化

    static{
      //代码
    }

静态初始化块执行的优先级高于非静态初始化块,在对象转载到JVM中时执行一次,仅能初始化类成员变量,即static修饰的数据成员

3. 非静态初始化

//定义
{
    //代码
}

非静态初始化在每个对象生产时都会被执行一次,它可以初始化类的实例变量。非静态初始化块在构造函数之前执行。

4. 代码案例

public class Test {
    static{
        System.out.println("static run");
    }

    {
        System.out.println("normal run");
    }

    public Test(){
        System.out.println("constructor run");
    }

    public static void main(String[] args)
    {
        Test t = new Test();

    }
}

运行结果:

static run
normal run
constructor run

Process finished with exit code 0

IDEA执行结果:

在这里插入图片描述
运行结果:
在这里插入图片描述

三. Java实例域初始化方法与类加载顺序

1. 非静态初始化块(构造代码块)
1)作用:
给对象进行初始化。对象一建立就运行,且优先于构造函数的运行。

2)非静态初始化块和构造函数的区别:
非静态初始化块给所有的对象进行统一初始化,构造函数只给对应对象初始化。

3)应用:
将所有构造函数共性的东西定义在构造代码块中。

2. 静态初始化块
1)作用:
给类进行初始化。随着类的加载而执行,且只执行一次。

2)静态初始化和静态初始化的区别:
1)构造代码块用于初始化对象,每创建一个对象就会被执行一次。静态代码块用于初始化类,随着类的加载而执行,不管创建几个对象,都只执行一次。
2)静态代码块优先于构造代码块的执行
3)都定义在类中,一个带static关键字,一个不带static关键字

总结
构造函数,非静态初始化块,静态代码块都是用于初始化,三者的执行顺序依次是:
静态代码块 – 》构造代码块 --》构造函数。其实初始化块就是构造器的补充,初始化块是不能接收任何参数的,定义的一些所有对象共有的属性,方法等内容时就可以用初始化块初始化了。

所有的静态初始化块都优先执行,其次才是非静态初始化块和构造函数,他们的执行顺序是:

  1. 父类的静态初始化块
  2. 子类的静态初始化块
  3. 父类的初始化块
  4. 父类的构造函数
  5. 子类的初始化块
  6. 子类的构造函数

其中,静态初始化块在大括号外加上static关键字,非静态代码初始化块一般叫做构造代码块。

总结:

1)静态初始化块的优先级最高,也就是最先执行,并且仅在类第一次被加载时执行。
2)非静态初始化块和构造函数后执行,并且在每次生成对象时执行一次
3)非静态初始化块的代码会在类构造函数之前执行。如果想要使用非静态初始化块的代码,一般可以写在构造函数之前,便于调试
4)静态初始化块可以初始化静态成员变量,也可以执行初始化代码
5)非静态初始化块可以针对多个重载构造函数进行代码复用

为什么静态成员变量只会初始化一次?
实际上,静态成员变量初始化的过程本质就是一个类加载和初始化的过程,虚拟机保证了在同一个类加载器下,一个类型只会初始化一次。

总结:

将类和和对象分开

  1. 类初始化
    1)静态成员变量初始化发生在静态方法之前
    2)父类的初始化必须在子类初始化之前
    3)静态成员变量的初始化顺序就是在代码中出现的顺序

  2. 实例化对象
    1)如果有父类,先执行父类的实例化
    2)成员变量初始化必须在构造函数之前
    3)成员变量的初始化顺序就是在代码中出现的顺序

  3. 实例话对象之前如果该类没有初始化,必须先执行该类的初始化

Java类的生命周期
一个class文件从加载到卸载的全过程,类的完整生命周期包括七个部分,分别是加载 --> 验证 --> 准备 -->解析 --> 初始化 --> 使用 --> 卸载。如图所示:

在这里插入图片描述

类加载过程

加载:通过类名获取类的二进制字节流是通过类加载器来完成的。其加载过程使用“双亲委派模型”

验证:当一个类被加载之后,必须要验证一下这个类是否合法,比如这个类是不是符合字节码的格式、变量与方法是不是有重复、数据类型是不是有效、继承与实现是否合乎标准等等。总之,这个阶段的目的就是保证加载的类是能够被JVM所运行。

准备:为类变量(静态变量)在方法区分配内存,并设置零值。注意:这里是类变量,不是实例变量,实例变量是对象分配到堆内存时根据运行时动态生成的。

解析:把常量池中的符号引用解析为直接引用:根据符号引用所作的描述,在内存中找到符合描述的目标并把目标指针指针返回。

初始化:类的初始化过程是这样的:按照顺序自上而下运行类中的变量赋值语句和静态语句,如果有父类,则首先按照顺序运行父类中的变量赋值语句和静态语句。在类的初始化阶段,只会初始化与类相关的静态赋值语句和静态语句,也就是有static关键字修饰的信息,而没有static修饰的赋值语句和执行语句在实例化对象的时候才会运行。执行()方法(clinit是class initialize的简写)

实例化:在堆区分配内存空间,执行实例对象初始化,设置引用变量a指向刚分配的内存地址

四. Java实例域初始化顺序

常用构造方法的具体处理步骤:
1)如果类是第一次被使用,先执行静态初始化块
2)所有数据域被初始化为默认值(0,false或null)
3)按照在类声明中出现的次序,依次执行所有域初始化语句和初始化块
4)如果构造方法调用了其他构造方法,先执行其他方法
5)最后,执行构造方法

五. Java常量,变量,对象(或字面量)在内存中的存储位置

常量方法区
静态变量方法区
局部变量
成员变量
常量且字面量常量池
常量且引用对象
静态变量且引用对象方法区
静态变量且字面量常量池
局部变量且引用对象
局部变量且基本类型字面量
局部变量且“”常量池
成员变量且引用对象
成员变量且基本类型字面量
成员变量且“”常量池

总结:new出的对象在存储堆中,”“存储常量池中,基本类型字面量为常量或静态变量时,存储在常量池,为成员变量存储堆中,为局部变量存储在栈中。

引用对象为静态变量方法区
引用对象为常量
引用对象为局部变量
引用对象为成员变量
基本类型字面量为常量常量池
基本类型字面量为静态变量常量池
基本类型字面量为局部变量
基本类型字面量为成员变量
字符串字面量任何时候常量池

参考文献:

  1. https://blog.csdn.net/fox_bert/article/details/108648886?utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control
  2. https://blog.csdn.net/Quincuntial/article/details/54291803
  3. https://juejin.cn/post/6844903538368184334
  4. https://www.jianshu.com/p/75ed7bf23d14
  5. https://www.cnblogs.com/pu20065226/p/12206463.html
  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值