美文网首页
Java初始化小析

Java初始化小析

作者: zcliu | 来源:发表于2017-11-08 21:11 被阅读0次

Java里有局部变量、实例变量、静态变量,它们的初始化表现也不尽相同。下面来总结一下。

局部变量

void f() {
    int i;
    i++; // Error -- i not initialized
}

局部变量应该是最简单的情景,在一个方法内定义一个变量,如果不为它赋予初始值,编译器则会报错。所以声明局部变量时必须同时赋予其初始值。

实例变量

每一个实例变量都会保证有一个初始值。

public class InitialiValues {

    boolean t;
    char c;
    byte b;
    short s;
    int i;
    long l;
    float f;
    double d;

    InitialiValues initialization;

    void printInitialValues() {
        System.out.println("boolean    " + t);
        System.out.println("char    " + c + "");
        System.out.println("byte    " + b);
        System.out.println("short    " + s);
        System.out.println("int    " + i);
        System.out.println("long    " + l);
        System.out.println("float    " + f);
        System.out.println("double    " + d);
    }

    public static void main(String[] args) {
        new InitialiValues().printInitialValues();
    }
}

/* Output */

boolean    false
char    null
byte    0
short    0
int    0
long    0
float    0.0
double    0.0
reference    null

即使不指定初始值,实例变量会自动拥有一个默认初始值

构造器初始化

public class Counter {

    int i;
    public Counter() {
        System.out.println("before: " +  i);
        i = 7;
        System.out.println("after: " + i);
    }

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

//Output:
before: 0
after: 7

我们可以利用构造器来进行初始化,但是在构造器进行初始化之前,counter已经自动初始化了一次。

初始化顺序

在一个类中,成员变量的初始化顺序是由变量在类中定义的顺序决定的,而且总会在类中的任何方法被调用前就已经初始化好了。

public class Window {

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

}

public class House {

    Window w1 = new Window(1);
    House() {
        System.out.println("House()");
        w3 = new Window(33);
    }
    Window w2 = new Window(2);
    void f() {
        System.out.println("f()");
    }
    Window w3 = new Window(3);

}

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

//Output:

Window(1)
Window(2)
Window(3)
House()
Window(33)
f()

可以看到,虽然Window对象的定义被打散了,但确实是等到所有的Windown变量初始化完成后,才执行构造器。

继承关系下的初始化顺序

在上面的基础上加上一个子类继承关系后,情况又会怎样?

public class SonHouse extends House{

    Window sonWindow = new Window(4);

    SonHouse() {
        sonWindow = new Window(44);
        }
    }
}

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

//Output:

Window(1)
Window(2)
Window(3)
House()
Window(33)
Window(4)
Window(44)
f()

事实证明,孝顺是一种优良品质,先让父类初始化完成后,再轮到子类

static变量的初始化时机

public class Bowl {

    Bowl(int marker) {
        System.out.println("Bowl(" + marker + ")");
    }

    void f1(int marker) {
        System.out.println("f1(" + marker + ")");
    }
}



public class Table {

    static Bowl bowl1 = new Bowl(1);
    Table() {
        System.out.println("Table()");
        bowl2.f1(1);
    }

    void f2(int marker) {
        System.out.println("f2(" + marker + ")");
    }
    static Bowl bowl2 = new Bowl(2);
}

public class Cupboard {

    Bowl bowl3 = new Bowl(3);
    static Bowl bowl4 = new Bowl(4);

    Cupboard() {
        System.out.println("Cupboard");
        bowl4.f1(2);
    }

    void f3(int marker) {
        System.out.println("f3(" + marker + ")");
    }
    static Bowl bowl5 = new Bowl(5);
}

public class StaticInitialization {

    public static void main(String[] args) {
        System.out.println("Creating new Cupboard() in main");
        new Cupboard();

        System.out.println("Creating new Cupboard() in main");
        new Cupboard();

        table.f2(1);
        cupboard.f3(2);
    }

    static Table table = new Table();
    static Cupboard cupboard = new Cupboard();
}

//Output:

Bowl(1)
Bowl(2)
Table()
f1(1)
Bowl(4)
Bowl(5)
Bowl(3)
Cupboard
f1(2)
Creating new Cupboard() in main
Bowl(3)
Cupboard
f1(2)
Creating new Cupboard() in main
Bowl(3)
Cupboard
f1(2)
f2(1)
f3(2)

可以看出,static变量总是最先初始化。要注意的是,static变量只会初始化一次,而且只会在第一次需要用到的时候才进行
初始化。然后才到 non-static变量初始化

就像上述示例,要执行main(),StaticInitialization类就需要被加载,然后其static变量 table和cupboad这个时候
同样需要被加载上,又因为它们都有static Bowl 变量,所以这是Bowl类也同时被加载。所以这些类在执行main方法前都已经
加载好了。

其实,学习过JVM话就会知道,一个class只有在第一次需要引用的时候才会被加载,以后需要用到这个class就直接从classloader
获取不需要加载了。而 static变量的初始化就是在class第一次被加载到classloader的时候发生的。所以不难理解为什么static变量
只会初始化一次。

总结

  1. 对于实例变量,按照声明的顺序逐个初始化,先父类,后子类

  2. static 成员在类第一次被加载时进行初始化,并且只初始化这么一次

相关文章

  • Java初始化小析

    Java里有局部变量、实例变量、静态变量,它们的初始化表现也不尽相同。下面来总结一下。 局部变量 局部变量应该是最...

  • GeekBand OC 5. 初始化器与析构器

    初始化器(初始化) 对象初始化器:-(id) 类型初始化器:+(void) 析构器(释放) 对象析构器:-(voi...

  • 《Swift从入门到精通》(十三):反初始化(析构过程)

    反初始化(析构过程)类实例销毁之前会立即调用析构器,用关键字 deinit 反初始化(析构过程)如何工作Swift...

  • Java内存模型小析

    Java虚拟机所管理的内存将会包括以下几个运行时数据区域:程序计数器(PC Register)、Java虚拟机栈、...

  • deinit析构函数

    init 构造函数,初始化方法 deinit 析构函数,反初始化方法 deinit 和OC中的 dealloc 一...

  • C++对象的初始化方式

    对象初始化可以分为默认初始化、直接初始化、拷贝初始化以及值初始化。C++的类默认提供了六种函数:构造函数、析构函数...

  • Java多线程相关要点小析

    Java多线程相关要点小析(本文系转载,仅供参考) synchronized和lock的区别 用法的区别 sync...

  • Chapter 13 Copying Controll

    这一章讲的更多是关于拷贝初始化, 拷贝赋值, 移动初始化, 移动赋值和析构的问题 拷贝初始化 相比于直接初始化的方...

  • kotlin属性初始化和懒加载之lateinit、by lazy

    java属性的初始化 在说kotlin属性初始化之前我们先来看下java属性的初始化 大体分为成员变量初始化、静态...

  • C++对象模型5——对象的构造/析构

    对象的构造/析构顺序 初始化虚基类,按照继承顺序,从左到右,从最深到最浅。 初始化按照继承顺序初始化父类,如果父类...

网友评论

      本文标题:Java初始化小析

      本文链接:https://www.haomeiwen.com/subject/pkqumxtx.html