java中类和对象的初始化

本文主要通过例子来理解java中类和对象的初始化

1、首先来看一个最简单的例子(无父类且无静态成员变量):
public class OrderOfInitialization1 {

    public static void main(String[] args) {
        House house = new House();
        house.f();
    }

}

class Window {
    Window(int market) {
        System.out.println("Window(" + market + ")");
    }
}

class House {
    Window w1 = new Window(1);//before constructor

    House() {
        //show we're in constructor
        System.out.println("House()");
        w3 = new Window(33);//reinitialize w3
    }

    Window w2 = new Window(2);//after constructor

    void f() {
        System.out.println("f()");
    }

    Window w3 = new Window(3);//at end
}
output:
    Window(1)
    Window(2)
    Window(3)
    House()
    Window(33)
    f()

从输出结果分析,House实例变量的初始化顺序是: w1w2w3 ,然后才是构造函数。

即:实例变量按照其在代码中出现的顺序执行初始化,然后执行构造函数里面的初始化。

2、接下来看一个稍微复杂一些的例子(有父类但无静态成员变量):
public class OrderOfInitialization2 {

    public static void main(String[] args) {
        SubClass subClass = new SubClass();
    }

}

class Print {
    Print(int i) {
        System.out.println("new Print(" + i + ")");
    }
}

class SuperClass {
    Print print1 = new Print(1);

    public SuperClass() {
        System.out.println("new SuperClass()");
    }

    Print print2 = new Print(2);
}

class SubClass extends SuperClass {
    Print print3 = new Print(3);

    public SubClass() {
        //这个地方其实是调用了父类的默认的无参构造函数,super();
        //如果父类没有无参构造函数,则这个地方必须显式的调用父类的构造函数,否则编译不通过
        System.out.println("new SubClass()");
    }

    Print print4 = new Print(4);
}
output:
    new Print(1)
    new Print(2)
    new SuperClass()
    new Print(3)
    new Print(4)
    new SubClass()

从输出结果分析:这个地方是先调用了父类 SuperClass 的构造函数,然后调用子类 SubClass 的构造函数。

即:如果一个类有父类,在实例化子类的时候,会先执行父类的构造函数,然后执行子类的构造函数。

3、继续看一个更复杂一些的例子(有父类且有静态成员变量):
public class OrderOfInitialization3 {

    public static void main(String[] args) {
        Man man = new Man();
        Man man1 = new Man();
    }

    static Print1 print0 = new Print1(0);
}

class Print1 {
    Print1(int i) {
        System.out.println("new Print1(" + i + ")");
    }
}

class People {
    Print1 print1 = new Print1(1);

    public People() {
        System.out.println("new People()");
    }

    Print1 print2 = new Print1(2);

    static Print1 print5 = new Print1(5);
}

class Man extends People {

    Print1 print3 = new Print1(3);

    public Man() {
        System.out.println("new Man()");
    }

    Print1 print4 = new Print1(4);

    static Print1 print6 = new Print1(6);
}
output:
       new Print(0)
       new Print(5)
       new Print(6)
       new Print(1)
       new Print(2)
       new People()
       new Print(3)
       new Print(4)
       new Man()
       new Print(1)
       new Print(2)
       new People()
       new Print(3)
       new Print(4)
       new Man()

从输出结果分析:这里首先执行了 OrderOfInitialization3 类的静态变量 print0 的初始化(输出 new Print(0) ),然后执行静态方法 main ;紧接着是执行 People 类的静态成员变量 print5 的初始化(输出 new Print(5) ),再接着是 Man 类的静态成员变量 print6 的初始化(输出 new Print(6) );之后是 People 的实例变量 (输出new Print(1)、new Print(2)) 、构造函数 (输出new People()) 初始化,最后才是 Man 实例变量 (输出new Print(3)、new Print(4)) 、构造函数 (输出new Man()) 的初始化。在第二次实例化一个 Man 的时候,所有的静态成员变量都没有相应的输出,即静态成员变量只初始化了一次。

所以这个地方执行的顺序是:首先执行 main 所在类的静态成员变量的初始化,然后是 Man 的父类的静态成员变量的初始化,然后是子类的静态成员的初始化;接着是父类的构造函数,最后才是子类的构造函数。

这个地方 Man 实例化了两次,但是其父类和本身的静态成员变量只初始化了一次。

为什么静态成员变量只会初始化一次呢?

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

这个地方把类和对象分开会更好理解一点。

  1. 类的初始化

    (1) 静态成员变量初始化发生在静态方法之前

    (2) 父类的初始化必须在子类初始化之前

    (3)静态成员变量的初始化顺序为其在代码中出现的顺序

  2. 实例化对象

    (1)如果有父类,先执行父类的实例化

    (2)成员变量初始化发生在构造函数之前

    (3)成员变量的初始化顺序为其在代码中出现的顺序

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

最后看一个比较特殊的例子:
public class NestedInitialization {

    public static void main(String[] args) {
        staticFunction();
    }

    static NestedInitialization st = new NestedInitialization();

    static {
        System.out.println("1");
    }

    {
        System.out.println("2");
    }

    NestedInitialization() {
        System.out.println("3");
        System.out.println("a=" + a + ",b=" + b);
    }

    public static void staticFunction() {
        System.out.println("4");
    }

    int a = 110;
    static int b = 112;

}
output:
    2
    3
    a=110,b=0
    1
    4

这个例子的特殊性在于,该类还没有完成初始化,就去实例化一个该类的对象。我们从类的生命周期来分析,一个类会经历加载、验证、准备、解析、初始化、使用和卸载七个阶段,在执行到 static NestedInitialization st = new NestedInitialization(); 这一步时,已经是类的初始化阶段了,此时,NestedInitialization 类里面的静态成员的值都还是准备阶段设置的初始零值,
static NestedInitialization st = null , static int b = 0; ,然后这个地方需要实例化 NestedInitialization ,所以是以此时的状态去实例化 NestedInitialization 类的,执行类的实例化,然后在继续类的初始化。所以才会出现上面的输出结果。

参考文献:
1. 《深入理解java虚拟机》周志明 著.
2. Thinking in Java(4th Edition)

发布了35 篇原创文章 · 获赞 24 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/haihui_yang/article/details/80544079
今日推荐