Java对象初始化过程代码块和构造器的调用顺序

 更新时间:2022年08月22日 08:59:23   作者:RichardGeek  
这篇文章主要介绍了Java对象初始化过程代码块和构造器的调用顺序,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下

前言

 对Java对象初始化过程 代码块与构造器调用顺序进行整理说明。先说结论具体论证在下文。

代码加载的优先级顺序 

静态代码块、静态成员变量->非静态代码块、非静态成员变量->new其他对象调用对应对象构造方法(在本地对象的方法外包括构造方法)->new本地对象调用构造方法。 

注意:若new对象时,该对象中有静态代码块和非静态代码块,每new一次对象,非静态代码块都会执行一次,但静态代码块只会执行一次往后new对象都不会再执行。】 

 构造方法的执行顺序 

父类静态代码块(静态变量 > 静态块) > 子类的静态代码块 > 父类构造代码块、构造方法> 子类的构造代码块、构造方法 

各种代码块的定义 

 静态代码块 

class Demo{ static { //静态代码块...... } }

特点:  1、Java静态代码块中的代码会在类加载JVM时运行,且只被执行一次 2、静态块常用来执行类属性的初始化 ,和一些全局初始化的工作 3、静态块优先于各种代码块以及构造函数,如果一个类中有多个静态代码块,会按照书写顺序依次执行 4、静态代码块可以定义在类的任何地方中除了方法体中【这里的方法体是任何方法体】 5、静态代码块不能访问普通变量

有关静态代码块再详细介绍下

静态代码块:在java中使用static关键字声明的代码块。静态块用于初始化类,为类的属性初始化。每个静态代码块只会执行一次。

由于JVM在加载类时会执行静态代码块,所以静态代码块先于主方法执行。 如果类中包含多个静态代码块,那么将按照"先定义的代码先执行,后定义的代码后执行"。 【注意:1 静态代码块不能存在于任何方法体内。2 静态代码块不能直接访问静态实例变量和实例方法,需要通过类的实例对象来访问。】 实例代码块 实例代码块 又叫 构造初始化块 , 构造代码块 , 初始化块 。

class Demo{ { //实例代码块...... } }

特点:

  • 1、构造代码块在创建对象时被调用,每次创建对象都会调用一次
  • 2、构造代码块优先于构造函数执行,同时构造代码块的运行依赖于构造函数
  • 3、构造代码块在类中定义

局部代码块

局部代码块又叫 普通代码块 , 方法代码块

class Demo{ public void test(){ { //局部代码块...... } } }

特点: 1、普通代码块定义在方法体中 2、普通代码块与实例代码块的格式一致都是{} 3、普通代码块与构造代码块唯一能直接看出的区别是构造代码块是在类中定义的,而普通代码块是在方法体中定义的 4、可以限定变量生命周期,及早释放,提高内存利用率

验证各代码块的执行顺序

举例代码如下:

class Init {
    public Init() {
        System.out.println("无参构造器");
    }
    public Init(int a) {
        System.out.println("有参构造器");
    }
    {
        System.out.println("实例代码块1");
    }
    {
        System.out.println("实例代码块2");
    }
    {
        System.out.println("实例代码块3");
    }
    static {
        System.out.println("静态初始化块1");
    }
 
    static {
        System.out.println("静态初始化块2");
    }

    public void method(){
    	{
    		System.out.println("普通初始化块");
    	}
    }
}

测试代码 如下:

class Demo {
    public static void main(String[] args) {
        Init init1 = new Init();
        init1.method();
        System.out.println("------------");
        Init init2 = new Init();
        init2.method();
        //多打印几个对象的目的是:方便看出Static静态代码块 是否只执行一次!!!
        System.out.println("------------");
        Init init3 = new Init();
        init3.method();
    }
}

运行结果如下图:

结论:

执行顺序为:静态代码块 > 实例代码块 > 构造函数 > 普通代码块,

且静态代码块,类加载的时候就会调用,且只调用一次(随着类的加载而执行)。

那么类什么时候会被加载呢?

- 创建对象实例时(new)
- 创建子类对象实例,父类也会被加载
- 使用类的静态成员时(静态属性,静态方法)

验证存在继承关系中各代码块的执行顺序

举例继承关系为 Three——> Two——> One,

代码如下:

class One {
    public One() {
        System.out.println("One构造器");
    }
 
    {
        System.out.println("One实例化块");
    }
 
    static {
        System.out.println("One静态代码块");
 
    }
 
}
class Two extends One {
 
    public Two() {
        System.out.println("Two构造器");
    }
 
    {
        System.out.println("Two实例化块");
    }
 
    static {
        System.out.println("Two静态代码块");
    }
 
}
 
class Three extends Two {
 
    public Three() {
        System.out.println("Three构造器");
    }
 
    {
        System.out.println("Three实例化块");
    }
    static {
        System.out.println("Three静态代码块");
    }
 
}
//测试代码 如下:
public class Demo {
    public static void main(String[] args) {
        Three three = new Three();
        System.out.println("-----");
        Three three1 = new Three(); //重复执行的目的是为了 验证static是否只执行一次
        System.out.println("-----");
        Two three2 = new Three();   //验证 多态的情况下 用后面的类进行初始化 结果和上面一样
    }
}

根据执行结果可知,在多个类的继承中存在初始化块、静态初始化块、构造器,执行真实顺序为:先后执行父类A的静态块,父类B的静态块,最后子类的静态块,然后再执行父类A的实例代码块和构造器,然后是B类的实例代码块和构造器,最后执行子类C的实例代码块和构造器【注:这里的ABC对应One、Two、Three 】

结论:

多个类的继承中初始化块、静态初始化块、构造器的执行顺序为:

父类静态块——>子类静态块——>父类实例代码块——>父类构造器——>子类实例代码块——>子类构造器 ——>(如果有局部代码块, 再正常执行即可, 这里就没必要进行测试了)

通过字节码深究实例代码块优先于构造器原因

我们那一段代码作为例子说明下,代码如下:

class Init {
    public Init() {
        System.out.println("无参构造器");
    }
    public Init(int a) {
        System.out.println("有参构造器");
 
    }
 
    {
        System.out.println("实例代码块1");
    }
 
    {
        System.out.println("实例代码块2");
    }
 
    {
        System.out.println("实例代码块3");
    }
 
    static {
        System.out.println("静态初始化块1");
    }
 
    static {
        System.out.println("静态初始化块2");
    }
 
    public void method(){
        {
            System.out.println("普通初始化块");
        }
    }
}

接下来让我们看看 , Init.java编译完的的字节码文件(Init.class)

从这个字节码文件就可以很清晰的看出, 实例代码块实际上是被依次放到了构造方法的第一句, 所以可以的出此结论: 实例代码块的执行顺序是优先于构造器的。

到此这篇关于Java对象初始化过程代码块和构造器的调用顺序的文章就介绍到这了,更多相关Java对象初始化过内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 轻松掌握Java适配器模式

    轻松掌握Java适配器模式

    这篇文章主要帮助大家轻松掌握Java适配器模式,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-09-09
  • Java导出CSV文件的方法

    Java导出CSV文件的方法

    这篇文章主要为大家详细介绍了Java导出CSV文件的方法,分页查询大数据量,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-12-12
  • java求解汉诺塔问题示例

    java求解汉诺塔问题示例

    汉诺塔问题的描述如下:有3根柱子A、B和C,在A上从上往下按照从小到大的顺序放着一些圆盘,以B为中介,把盘子全部移动到C上。移动过程中,要求任意盘子的下面要么没有盘子,要么只能有比它大的盘子。编程实现3阶汉诺塔的求解步骤
    2014-02-02
  • java导出生成csv文件的方法

    java导出生成csv文件的方法

    这篇文章主要为大家详细介绍了java导出生成csv文件的相关资料,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-01-01
  • Java求两集合的交集、并集、差集实例

    Java求两集合的交集、并集、差集实例

    这篇文章主要介绍了Java求两集合的交集、并集、差集实例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-08-08
  • Spring通过c3p0配置bean连接数据库

    Spring通过c3p0配置bean连接数据库

    这篇文章主要为大家详细介绍了Spring通过c3p0配置bean连接数据库,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-08-08
  • 解决@Autowired注入static接口的问题

    解决@Autowired注入static接口的问题

    这篇文章主要介绍了解决@Autowired注入static接口的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • Java JDK动态代理(AOP)用法及实现原理详解

    Java JDK动态代理(AOP)用法及实现原理详解

    在本篇文章了小编给大家整理的是一篇关于Java JDK动态代理(AOP)用法及实现原理详解内容,有需要的朋友们可以参考学习下。
    2020-10-10
  • java如何根据提供word模板导出word文档详解

    java如何根据提供word模板导出word文档详解

    在日常的开发工作中,我们时常会遇到导出Word文档报表的需求,比如公司的财务报表、医院的患者统计报表、电商平台的销售报表等等,这篇文章主要给大家介绍了关于java如何根据提供word模板导出word文档的相关资料,需要的朋友可以参考下
    2023-09-09
  • 解决日期转化Json异常- Date JSON parse error

    解决日期转化Json异常- Date JSON parse error

    这篇文章主要介绍了解决日期转化Json异常- Date JSON parse error问题。具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-06-06

最新评论