- 类的初始化步骤
- 加载:将.class文件从磁盘加载到内存,然后在堆区创建一个java.lang.Class对象,这个对象封装了类的全部信息。
- 连接:确保被加载的.class文件符合规范,如果符合规范则为类的静态变量分配内存并赋予默认值。
- 初始化:为类的静态变量赋予正确的值(所谓正确是指用户赋予的值)。
在连接过程中,静态变量的默认值赋值规则为:整形赋值为0,浮点型赋值为0.0,布尔型赋值为false,引用类型赋值为null。如,
public class Test1 { private static int i;// 默认初始化为0 private static short j;// 默认初始化为0 private static long k;// 默认初始化为0 private static byte o;// 默认初始化为0 private static String str;// 默认初始化为null private static boolean flag;// 默认初始化为false private static float f;// 默认初始化为0.0 private static double d;// 默认初始化为0.0 public static void main(String args[]) { init(); } private static void init() { System.out.println("int i= " + i + " short j= " + j + " long k= " + k + " byte o= " + o + " String str=" + str + " flag= " + flag + " float f=" + f + " double d=" + d); } }
输出结果为:int i= 0 short j= 0 long k= 0 byte o= 0 String str=null flag= false float f=0.0 double d=0.0
类的静态变量首先按照上述规则赋予默认值,然后才会初始化为自定义的值,如,/** * 加载的时候给静态变量赋予默认值 然后 初始化自定义的值 * @lsl */ class ClassLoaderTest { private static ClassLoaderTest clt=new ClassLoaderTest(); public static int counter1; public static int counter2=0; private ClassLoaderTest(){ counter1++; counter2++; } public static ClassLoaderTest getInstance(){ return clt; } } public class ClassLoaderTestMain{ public static void main(String args[]){ ClassLoaderTest clt=ClassLoaderTest.getInstance();//主动使用:调用类的静态方法(会引起类的初始化) System.out.println("counter1= "+clt.counter1); System.out.println("counter2= "+clt.counter2); } }
输出结果为:counter1= 1 counter2= 0
执行顺序为:第一步,为静态变量赋予默认值,clt=null,counter1=0,counter2=0;
第二步,为静态变量赋予自定义值,首先执行构造函数,使得counter1=1,counter2=1,然后执行
第一行不做任何操作,第二行将counter2赋值为0。可以进一步想象,如果第一句修改为 public static int counter1=2; 则输出结果将是counter1=2,counter2=0;public static int counter1; public static int counter2=0;
- 对类的主动使用会导致类的初始化操作,主动使用一共包括六种
- 虚拟机启动时被作为启动类的类;
- 创建类的实例(new Object(););
- 访问类的静态变量、或者为静态变量赋值;
- 调用类的静态方法;
- 初始化一个类的子类时对父类的主动使用;
- 通过反射使用该类。
第一种,也是最为直观的一种,执行启动类(包含main函数的public类)会导致该启动类初始化。如,
public class Child { public Child() { System.out.println("constructor of class Child"); } static { System.out.println("static block of class Child"); } public static void main(String[] args) {//启动类入口 } }
输出结果为:注意类的初始化并没有执行构造函数,而是仅针对静态块和静态变量。static block of class Child
第二种,创建类的实例,会导致类的初始化。如,
class Parent { static int i; static { System.out.println(i+"-static block of class Parent"); } public Parent() { System.out.println("constructor of class Parent"); } public static void method() { System.out.println("method of class Parent"); } } public class Child { public Child() { System.out.println("constructor of class Child"); } static { System.out.println("static block of class Child"); } public static void main(String[] args) { new Parent();//创建类的实例 } }
输出结果为:static block of class Child 0-static block of class Parent constructor of class Parent
注意初始化多个类时,类间的初始化顺序,以及new一个对象时,静态语句和构造函数的执行顺序。
第三种,访问类的静态变量,或者为类的静态变量赋值,会导致类的初始化。如,
class Parent { static int i; static { System.out.println(i+"-static block of class Parent"); } public Parent() { System.out.println("constructor of class Parent"); } public static void method() { System.out.println("method of class Parent"); } } public class Child { public Child() { System.out.println("constructor of class Child"); } static { System.out.println("static block of class Child"); } public static void main(String[] args) { System.out.println(Parent.i);//访问类的静态变量 } }
输出结果为:static block of class Child 0-static block of class Parent 0
这一种也比较容易理解,类的初始化是初始化类的静态变量,或者静态块,当你访问一个类的静态变量时,被访问的静态变量所在类会初始化该变量,以免无法访问。
第四种,调用类的静态方法会导致类的初始化。如,
输出结果为:class Parent { static int i; static { System.out.println(i+"-static block of class Parent"); } public Parent() { System.out.println("constructor of class Parent"); } public static void method() { System.out.println("method of class Parent"); } } public class Child { public Child() { System.out.println("constructor of class Child"); } static { System.out.println("static block of class Child"); } public static void main(String[] args) { Parent.method();//调用类的静态方法 } }
static block of class Child 0-static block of class Parent method of class Parent
第五种,初始化子类会导致父类的初始化。
class Parent { static int i; static { System.out.println(i+"-static block of class Parent"); } public Parent() { System.out.println("constructor of class Parent"); } public static void method() { System.out.println("method of class Parent"); } } public class Child extends Parent{ public Child() { System.out.println("constructor of class Child"); } static { System.out.println("static block of class Child"); } public static void main(String[] args) {//作为启动类 } }
输出结果为:0-static block of class Parent static block of class Child
可见,初始化子类时,会首先初始化父类。( 注意,初始化时针对静态变量或静态代码块,构造函数在初始化时不一定执行,除非是通过创建对象这种方式进行初始化)
第六种,反射是对类的主动使用,会导致类的初始化。如,
package jvm; class Parent { static int i; static { System.out.println(i+"-static block of class Parent"); } public Parent() { System.out.println("constructor of class Parent"); } public static void method() { System.out.println("method of class Parent"); } } public class Child extends Parent{ public Child() { System.out.println("constructor of class Child"); } static { System.out.println("static block of class Child"); } public static void main(String[] args) throws ClassNotFoundException { Class.forName("jvm.Parent");//反射 } }
输出结果为:0-static block of class Parent static block of class Child
----------------------End----------------------除了上述6种对类的主动使用外,其它对类的使用(Object object;)都不会引起类的初始化操作。
(总结不当之处,敬请指正)