前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【java基础☞初始化顺序】java继承中的初始化顺序

【java基础☞初始化顺序】java继承中的初始化顺序

作者头像
用户5640963
发布2019-07-25 11:17:46
9430
发布2019-07-25 11:17:46
举报
文章被收录于专栏:卯金刀GG卯金刀GG卯金刀GG

1、初始化顺序:父类的静态变量-->父类的静态代码块-->子类的静态变量-->子类的静态代码快-->父类的非静态变量(父类的非静态代码块)-->父类的构造函数-->子类的非静态变量(子类的非静态代码块)-->子类的构造函数

重点关注:父类的非静态变量(父类的非静态代码块)是同一级的,看哪个部分写在类的最前面谁就先被执行,子类的非静态变量(子类的非静态代码块)也是一样。

package com.test;


public class Test {
    public static void main(String[] args) {
        Child c=new Child();
    }
}
class Parent {
    public static PrintMessage a=new PrintMessage("父类静态成员被初始化");
    private PrintMessage b=new PrintMessage("父类非静态成员被初始化"); 
    static{
        System.out.println("父类的静态代码块被执行");
    }
    {
        System.out.println("父类的非静态代码块被执行");
    }
    public Parent(){
        System.out.println("父类的构造方法被执行");
    }   
}

class Child extends Parent{
    public static PrintMessage a1=new PrintMessage("子类静态成员被初始化");
    private PrintMessage b1=new PrintMessage("子类非静态成员被初始化");    
    
    static {
        System.out.println("子类的静态代码块被执行");
    }
    {
        System.out.println("子类的非静态代码块被执行");
    }
    public Child(){
        System.out.println("子类的构造函数被执行");
    }
}

class PrintMessage{
    public PrintMessage(String mes){
        System.out.println(mes);
    }
}

执行结果:

父类静态成员被初始化 父类的静态代码块被执行 子类静态成员被初始化 子类的静态代码块被执行 父类非静态成员被初始化 父类的非静态代码块被执行 父类的构造方法被执行 子类非静态成员被初始化 子类的非静态代码块被执行 子类的构造函数被执行

2、JAVA 类的加载机制

Java类加载分为5个过程,分别为:加载,连接(验证,准备,解析),初始化,使用,卸载。

1. 加载 加载主要是将.class文件(也可以是zip包)通过二进制字节流读入到JVM中。 在加载阶段,JVM需要完成3件事: 1)通过classloader在classpath中获取XXX.class文件,将其以二进制流的形式读入内存。 2)将字节流所代表的静态存储结构转化为方法区的运行时数据结构; 3)在内存中生成一个该类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。

2.连接

2.1. 验证 主要确保加载进来的字节流符合JVM规范。验证阶段会完成以下4个阶段的检验动作: 1)文件格式验证 2)元数据验证(是否符合Java语言规范) 3)字节码验证(确定程序语义合法,符合逻辑) 4)符号引用验证(确保下一步的解析能正常执行) 2.2. 准备 准备是连接阶段的第二步,主要为静态变量在方法区分配内存,并设置默认初始值。 2.3. 解析 解析是连接阶段的第三步,是虚拟机将常量池内的符号引用替换为直接引用的过程。

3. 初始化

初始化阶段是类加载过程的最后一步,主要是根据程序中的赋值语句主动为类变量赋值。 当有继承关系时,先初始化父类再初始化子类,所以创建一个子类时其实内存中存在两个对象实例。 注:如果类的继承关系过长,单从类初始化角度考虑,这种设计不太可取。原因我想你已经猜到了。 通常建议的类继承关系最多不超过三层,即父-子-孙。某些特殊的应用场景中可能会加到4层,但就此打住,第4层已经有代码设计上的弊端了。

4. 使用 程序之间的相互调用。

5. 卸载 即销毁一个对象,一般情况下中有JVM垃圾回收器完成。代码层面的销毁只是将引用置为null。

通过上面的整体介绍后,再来看Singleton2.getInstance()的执行分析: 1)类的加载。运行Singleton2.getInstance(),JVM在首次并没有发现Singleton类的相关信息。所以通过classloader将Singleton.class文件加载到内存中。 2)类的验证。略 3)类的准备。将Singleton2中的静态资源转化到方法区。value1,value2,singleton在方法区被声明分别初始为0,0,null。 4)类的解析。略(将常量池内的符号引用替换为直接引用的过程) 5)类的初始化。执行静态属性的赋值操作。按照顺序先是value1 = 5,value2 = 3,接下来是private static Singleton2 singleton2 = new Singleton2(); 这是个创建对象操作,根据 结论1 在执行Singleton2的构造方法之前,先去执行static资源和非static资源。但由于value1,value2已经被初始化过,所以接下来执行的是非static的资源,最后是Singleton2的构造方法:value1++;value2++。 所以Singleton2结果是6和4。

以上除了搞清楚执行顺序外,还有一个重点->结论2:静态资源在类的初始化中只会执行一次。不要与第3个步骤混淆。

有了以上的这个结论,再来看Singleton.getInstance()的执行分析: 1)类的加载。将Singleton类加载到内存中。 2)类的验证。略 3)类的准备。将Singleton2的静态资源转化到方法区。 4)类的解析。略(将常量池内的符号引用替换为直接引用的过程) 5)类的初始化。执行静态属性的赋值操作。按照顺序先是private static Singleton singleton = new Singleton(),根据 结论1结论2,value1和value2不会在此层执行赋值操作。所以singleton对象中的value1,value2只是在0的基础上进行了++操作。此时singleton对象中的value1=1,value2=1。 然后, public static int value1 = 5; public static int value2 = 3; 这两行代码才是真的执行了赋值操作。所以最后的结果:5和3。 如果执行的是public static int value1; public static int value2;结果又会是多少?结果: 1和1。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 重点关注:父类的非静态变量(父类的非静态代码块)是同一级的,看哪个部分写在类的最前面谁就先被执行,子类的非静态变量(子类的非静态代码块)也是一样。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档