浅谈Java中数据的初始化顺序(含静态数据的初始化)

第二天,哼着小曲来到教室,摘下眼镜擦眼镜,准备上课。一抬头看见了一个大长腿,艾玛,真好看。

看够了没!lsp。

我:啊,看够了...啊,不是不是(慌忙带上眼镜),哟!冉冉大妹纸。

冉冉:谁是你大妹纸啊,往里坐坐。

我:好勒!

冉冉:我昨天回去看了看一条不会写作的码农写的简单理解Java的构造器,把构造函数给整明白了,但是问题来了,什么初始化,还有什么顺序,真让人头疼,给我讲讲?

我:叫哥哥给你讲。

冉冉:算了,我找别人吧。

我:今天我要是给你讲不明白,我就不放学!

今天要讲的重点

  • 成员变量默认初值
  • 构造器初始化以及初始化顺序
  • 静态数据的初始化

一、成员变量默认初值

要知道:只有成员变量,在运行时期才会进行赋予默认的初始值。Java要尽量保证所有变量在使用前都能得到恰当的初始化。而对于方法中含有的局部变量,Java则不会给默认值,而是以编译错误的形式,来保证局部变量的初始化;

下面来看看Java给的默认初始值是多少

boolean : false
char    : [ ]
byte    : 0
short   : 0
int     : 0
long    : 0
float   : 0.0
double  : 0.0
reference(引用类型(可以理解为对象)) : null 

冉冉:这个char为一个中括号是什么意思?

我:这个啊,char的初始值也默认为0,所以显示为空白了

这是Java默认给你的初始值,当然如果你在方法中这样定义,那编译器肯定有点小脾气。

冉冉:嗯?你怎么没说String呢?

我:你说String是什么类型的?

冉冉:当然基本数据类型呀(脱口而出)

我:鼻子给你打歪,你家的Java是九种基本数据类型啊,他也是个引用类型啊,不然你以为你怎么能写

String str = new String();

这样的代码啊;好了,咱继续扯;

二、构造器初始化以及初始化顺序

  • 构造器初始化

我:昨天你看了那什么什么码农的构造器,那我就不再给你啰嗦了,直接说重点吧?

冉冉:完全没问题!

我:好!废话不多说,开始,看下边的代码

public class Student {
    private String username;
    private String password;
    
    public Student(String username,String password){
        this.username = username;
        this.password = password;
    }
}

冉冉:哎哎哎!你也看过那篇文章?

我:嘿嘿,那必须啊,他多帅啊,我也关注了,继续继续!

这是使用构造函数进行的初始化变量,当你调用这个构造函数的时候,传入的两个参数,就是初始化的值,测试一下

public class Student {
    private String username;
    private String password;

    Student(String username, String password) {
        this.username = username;
        this.password = password;
    }
    public static void main(String[] args) {
        Student testMain2 = new Student("1","1");
        System.out.println(testMain2.username);
        System.out.println(testMain2.password);
    }
}

又来了一个重点:当你进行初始化的时候,username和password的值首先会被置为null,然后才变成了"1","1"。对于所有基本类型和对象引用,包括在定义时已经设置了初值变量,这种情况都是成立的。

我:理解了这个知识点,那咱们进入下一个,也是你头疼的地方,初始化的顺序。打什么瞌睡,仔细听讲了!

  • 初始化顺序

在类的内部,变量定义的先后顺序决定了他们的初始化顺序,即顺序初始化,即使他们分散在各个方法之间,也会在第一时间进行初始化(注意:初始化要优先于方法加载,包括构造方法)

冉冉:有点不理解分散在各个方法之间

我:看代码就理解了,把上边的代码做个简单的改造

public class Student {
    private String username;
    private String password;

    Student(String username, String password) {
        this.username = username;
        this.password = password;
    }
    int i;
    void f(){}
    boolean b;
}

这就叫做分散各个方法之间。这时候的初始化顺序,并不会因为构造函数在变量 i 之前,构造方法就先初始化;懂了吧!

冉冉:明白了

那咱们继续:

public class TestMain {
    public static void main(String[] args) {
        House h = new House();
        h.f();
    }
}
class Window {
    Window(int marker) {
        System.out.println("Window方法的构造器" + marker);
    }
}
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);
}

这段代码是怎么执行的呢,来分析分析吧?

冉冉:好的,我试试看

(几分钟后,娓娓道来)首先main方法中 new了个House的对象,然后根据你刚刚说的,先初始化所有的变量,这时候会执行

Window w1 = new Window(1);

Window w2 = new Window(2);

Window w3 = new Window(3);

每次执行,都会去调用Window类中的构造方法,输出一句 "Window方法的构造器" + 所传的 1 2 3

紧接着在 加载House() 这个构造方法,输出一个 "House()" 再执行 w3 = new Window(33); 输出一个"Window方法的构造器33"

最后调用了 f() 这个方法,输出了"f()";

对不对,对不对。冉冉激动的说到

我:不错不错,孺子可教也,看下结果吧!

在整个程序执行的过程中 w3 会被初始化两次:一次在调用构造器前,一次在调用期间,而第一次的对象被无情的抛弃了,被当成了垃圾给运送走了。

冉冉:终于弄明白了,你挺厉害的呀!

我:那何止厉害,我还帅呢!别急,还没结束,还有一个重点呢,咱继续掰扯!

三、静态数据的初始化

我:知道什么是静态吗?

冉冉:知道知道,static关键字嘛。

我:嗯对,就是这个关键字,既然知道,那我就不给你讲基础了吧,直接说重点!

首先呢,你要知道一点:无论你创建多少个对象,静态数据都只会占用一份存储区域。而且static关键字不能用于局部变量。

看下面的例子,了解一下初始化顺序:

public class TestMain {
    public static void main(String[] args) {
        System.out.println("new 一次Cupboard()");
        new Cupboard();
        System.out.println("new 二次Cupboard()");
        new Cupboard();
        table.f2(1);
        cupboard.f3(1);

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

}
class Bowl {
    Bowl(int i) {
        System.out.println("bowl类的构造方法--->" + i);
    }
    void f1(int i) {
        System.out.println("方法f1() ---> " + i);
    }
}
class Table {
    static Bowl bowl1 = new Bowl(1);
    Table() {
        System.out.println("Table类的构造方法");
        bowl2.f1(1);
    }
    void f2(int i) {
        System.out.println("方法f2() ---> " + i);
    }
    static Bowl bowl2 = new Bowl(2);
}
class Cupboard {
    Bowl bowl3 = new Bowl(3);
    static Bowl bowl4 = new Bowl(4);
    Cupboard() {
        System.out.println("Cupboard类的构造方法");
        bowl4.f1(2);
    }
    void f3(int i) {
        System.out.println("方法f3() ---> " + i);
    }
    static Bowl bowl5 = new Bowl(5);
}

这个我给你分析,你听仔细哦~,不要走神,只讲一遍

冉冉:好(打起精神)

首先,主函数作为入口函数,先初始化静态变量,即执行 static Table table = new Table(); 执行这条语句之后,就会加载Table类,这是一定的,即使不是一个静态,也会加载这个类,然后再去执行 static Bowl bowl1 = new Bowl(1); 此时就会去加载Bowl类的构造方法,从而输出了一句 "bowl类的构造方法 ---> 1" ,然后程序继续加载Table了中的静态变量:

static Bowl bowl2 = new Bowl(2);  再次调用Bowl的构造方法,输出"bowl类的构造方法 ---> 2",然后才加载Table类的构造方法,输出"Table类的构造方法",然后执行 bowl2.f1(1); 

程序回到主函数,再执行 static Cupboard cupboard = new Cupboard(); 然后加载Cupboard类,接着加载类中的静态变量,重复上边的情况,按顺序执行。这里注意的是,执行完两个静态变量,会接着执行非静态的,即 Bowl bowl3 = new Bowl(3);

执行完static Cupboard cupboard = new Cupboard();程序再次回到主函数,执行第一个输出语句,紧接着,执行

new Cupboard(); 你说说,下一步该执行什么了?冉冉同学

冉冉:下一步啊,这还不简单,先执行静态变量,执行完,再执行非静态的,然后再执行构造方法呗,这有啥难的。

我:不对哦~,(挖个坑你就跳,这还不是随随便便就把你骗到手了么,哈哈哈)

重点:被static修饰的变量,本质上,只会在类加载的时候加载一次,所以嘛,直接就会执行 Bowl bowl3 = new Bowl(3);

同样的,你再次回到主函数,再 new Cupboard(); 也还会是这样执行的

冉冉:戚,你也就欺负欺负我这种小白了,啥也不是,再见!(说着起身就要走)

我:哎哎哎,别生气啊,我给你总结一下啊!

冉冉:不用!哼!

四、总结

所以,现在来看,初始化顺序就显而易见了  静态变量 --> 非静态变量 --> 构造方法

所有文字,代码,均为手敲,如有错误,或补充,欢迎指出,一定及时改正!

来都来了,点个关注,点个赞呗!

猜你喜欢

转载自blog.csdn.net/weixin_44231805/article/details/109223387