- 类的初始化步骤
- 加载:将.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,然后执行
public static int counter1; public static int counter2=0;第一行不做任何操作,第二行将counter2赋值为0。可以进一步想象,如果第一句修改为 public static int counter1=2; 则输出结果将是counter1=2,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----------------------