Java 对象初始化

Java 对象初始化

当我门创建某个对象 new ClassName(), 或者是访问某个类的静态方法 ClassName.staticFields 的时候就需要初始化一个类

Step1 定位 .class 文件

Java 解释器去寻找类的路径,找到编译好的 .class 文件。若有父类则先定位父类的 .class 文件。

Step2 加载类资源

这里的类资源就是指类中的静态方法、静态变量,这些种资源属于类不属于特定的对象,只在类首次加载的时候初始一次,存放在 JVM 的方法区(静态区)。

Step3 实例化对象

  • 先进入构造函数,还没执行其中的语句
  • 分配堆空间,属性/方法设置默认值
  • 检查是否有父类,有父类就先加载父类的构造函数
  • 执行构造函数内部的操作

有父类的情况

在找出子类的class文件后通过extends关键字发现,它有父类,那么就先加载父类的静态资源,接在结束之后再加载子类的静态资源。

加载子类的资源时实际上就进入了main函数了,遇到new Son()的语句时,找到构造函数先为Son分配相应的存储空间,接着先执行隐含的super(),即前往父类的构造函数,完成父类的字段/方法构造后,再开始的子类的字段/方法初始,最后执行构造函数中的语句。

简而言之:父类加载资源 \(\rightarrow\) 子类加载资源 \(\rightarrow\) 对象实例化(父类成员(字段/方法) > 父类构造器 > 子类成员 > 子类构造器)

class Shape {

    /**
     * 8. 初始创建好的字段方法
     */
    private int field = printInit("@Father Field");

    private void method() {
    }

    /**
     * 7. 构造对象(实例化) 创建字段、方法 
     * 完成后发现自己已经是祖宗了(其实往上还有 Object,但这里忽略)
     */
    Shape() {
        /** 9. 执行构造函数中的语句 */
        System.out.println("@Father Constructor: i = " + i + ", j = " + j);
        j = 5;
    }

    /**
     * 2. 顺序加载父类的资源 注意由于赋值是右边赋值给左边,所以实际上是 printInit 先加载完毕
     */
    private static int i;
    protected static int j = 10;
    private static int x1 = printInit("@Father Static Source: private Shape.x1 initialized Static Source");

    static int printInit(String s) {
        System.out.println(s);
        return ++i;
    }
}

public class Circle extends Shape {
    /** 10. 初始化字段和方法 */
    int k = printInit("@Son Field: Circle.k initialized");

    /**
     * 6. 进入构造函数,开始创建对象(实例化) 
     * 分配存储空间 -> 创建字段和方法(还没有赋值,但已经存在) 先执行 super() 父类的构造
     */
    public Circle() {
        /** 11. 执行构造函数中的语句 */
        System.out.println("@Son Constructor: k = " + k);
        System.out.println("j = " + j);
        System.out.println("x2 = " + x2);
    }

    /**
     * 3. 父类的资源加载完后,子类加载资源 
     * 类的资源就是静态资源,对象的资源就是普通字段/方法 main 函数也是静态资源!
     */
    public static int x2 = printInit("@Son Static Source: public Circle.x2 initialized Static Sourcce");

    /**
     * 1. 要加载 main 函数(子类的static资源)就要先加载 Circle.class 
     * 发现有父类 Shape,所以先加载 Shape.class
     */

    public static void main(String[] args) {
        /** 4. 执行 main 函数 */
        System.out.println("@Son Static Source: main method");
        /** 5. 遇到 new Circle() 要调用构造函数 */
        Circle b = new Circle();
        /** 12. b 对象构造完成继续下一条语句 */
        System.out.println("@Keep Going in main: b.k=" + b.k);
    }
}

/*
@Father Static Source: private Shape.x1 initialized Static Source
@Son Static Source: public Circle.x2 initialized Static Sourcce
@Son Static Source: main method
@Father Field
@Father Constructor: i = 3, j = 10
@Son Field: Circle.k initialized
@Son Constructor: k = 4
j = 5
x2 = 2
@Keep Going in main: b.k=4
*/

posted @ 2020-03-08 17:17  Bankarian  阅读(200)  评论(0编辑  收藏  举报