我们知道一个类中具有类变量、类方法和构造器(方法中的局部变量不讨论,他们是在方法调用时才被初始化),当我们初始化创建一个类对象时,其初始化的顺序为:先初始化类变量,再执行构造器方法。
代码验证:
public class Demo01 { public int a1 = 1; public String a2 = "initiaied!"; public Demo01() { System.out.println(a1); System.out.println(a2); System.out.println("构造器方法被执行"); } public static void main(String[] args) { Demo01 demo01 = new Demo01(); } } 运行结果: 1 initiaied! 构造器方法被执行
可以看出,当我们创建一个Demo01对象时,先初始化了变量a1和a2,然后执行构造器方法。
静态变量是属于类本身,无论创建多少个对象,静态变量都只有一份存储区域,因此他会在类首次被访问或者首次创建类对象时被初始化,而且有且只能初始化一次。
而非静态变量是属于每个对象,他是在创建每个对象时都初始化一次。因此,静态变量要先于非静态进行初始化。例子:
public class Demo02 { public Cup cup1 = new Cup(1); public static Cup cup2 = new Cup(2); public Demo02() { System.out.println("Demo02构造器被执行!"); } public static void main(String[] args) { Demo02 demo02 = new Demo02(); Demo02 demo02_01 = new Demo02(); } } class Cup { public Cup(int i) { System.out.println("Cup->" + i); } } 运行结果: Cup->2 Cup->1 Demo02构造器被执行! Cup->1 Demo02构造器被执行
当程序要执行main方法时,会先加载Demo02类文件,在加载时就会先初始化静态变量cup2,因此控制台输出"cup->2"。
类加载完后,开始执行main方法,创建demo02对象,这时就会初始化类中的非静态变量cup1,控制台输出"cup->1",然后执行构造器方法。创建第二个对象时,只初始化cup1,cup2为静态变量只初始化一次。
静态代码块本质上就是一段静态变量的代码,其初始化和静态变量没有区别:当类首次被访问或者首次创建该类对象时被初始化,并且只初始化一次。
非静态代码块就是一段非静态变量的代码,他和非静态变量的初始化没有区别。
public class Demo03 { static Table table1; Table table2; static { table1 = new Table(1); } { table2 = new Table(2); } public Demo03() { System.out.println("Demo03构造器被执行"); } public static void main(String[] args) { new Demo03(); } } class Table { public Table(int i) { System.out.println("Table->" + i); } } 运行结果: Table->1 Table->2 Demo03构造器被执行
没有父类就没有子类,因此无论是类加载还是创建实例对象,父类都优先于子类进行初始化。
public class Demo04 extends Insect { private int k = fun("Demo04中的k被初始化"); private static int x2 = fun("Demo04中的x2被初始化"); public Demo04() { System.out.println("k=" + k); System.out.println("j=" + j); } public static void main(String[] args) { Demo04 demo04 = new Demo04(); } } class Insect { public int i = 9; public int j; public static int x1 = fun("Insect中的x1被初始化"); public Insect() { System.out.println("i=" + i + ",j=" + j); j = 39; } public static int fun(String s) { System.out.println(s); return 47; } } 运行结果: Insect中的x1被初始化 Demo04中的x2被初始化 i=9,j=0 Demo04中的k被初始化 k=47 j=39
当执行main方法时,加载器开始加载Demo04类文件,在加载过程中,加载器会注意到他有一个父类Insect还没被加载,因此会先加载父类Insect文件。
类加载过程中会完成静态变量的初始化(此时并不执行构造器方法,构造器方法只有在创建对象时调用),Insect类加载完成后,接着加载Demo04类,都加载完成后,就开始执行main方法中的代码,创建Demo04对象。
由于继承关系,因此先创建父类Insect的实例对象,因此父类中的变量初始化和构造器先被执行,然后在初始化子类Demo04中的非静态变量和执行构造器方法。
最后放一段代码,把前面所说情况都放在一起。
public class Son extends Father { int m = fun("Son中的m 被初始化"); public Son() { System.out.println("m = " + m); System.out.println("j = " + j); } public static int x3 = fun("Son中的x3 被初始化"); public static void main(String[] args) { Son son = new Son(); } } class Father extends GrandFather { public int k = fun("Father中的k被初始化"); public static int x2 = fun("Father中的x2被初始化"); public Father() { System.out.println("k=" + k); System.out.println("j=" + j); } } class GrandFather { public int i = 9; public int j; public static int x1 = fun("GrandFather中的x1被初始化"); public GrandFather() { System.out.println("i=" + i + ",j=" + j); j = 39; } public static int fun(String s) { System.out.println(s); return 47; } } 运行结果: GrandFather中的x1被初始化 Father中的x2被初始化 Son中的x3 被初始化 i=9,j=0 Father中的k被初始化 k=47 j=39 Son中的m 被初始化 m = 47 j = 39