Java虚拟机:类的初始化

时间:2022-12-27 15:14:27

一、 类的初始化是在类加载的最后阶段进行的.对一个进行主动引用时,才会进行此类的初始化,Java虚拟机规定只有下面四种情况下才会进行类的初始化:

1 当虚拟机执行:new getstatic putstatic invokestatic 四个字节码指令时,才会进行类的初始化.这个四种字节码指令当new一个对象,读取或设置一个静态变量,调用一个静态方法时就会产生.

2.当使用Java.lang.refelect 包进行反射调用是,如果类还没进行初始化,就会先进行初始化.

3.当对子类进行初始化时,如果他的父类还没有进行初始化,那么先对父类进行初始化.

4.Java虚拟机启动时,需要一个主函数,先对主函数类进行初始化.
因此以上四种情况,对一个类的引用称之为主动引用.

二、当对一个类进行被动引用是,虚拟机并不会对类进行初始化,以下是被动引用的情况:

1.在一个子类中进行对父类的静态变量或者静态方法的引用,此时这个子类属于被动引用,并不会对子类进行初始化.
对于静态变量或者静态方法,只有直接定义这个静态变量和静态方法的类才会被初始化.因此通过子类引用父类中定义静态变量或静态方法,只会触发父类的初始化.
如下代码:

public class SuperClass {

static{
System.out.println("父类初始化");
}

public static int s =1;

public static String getString(){
return "10";
}

}

public class SubClass extends SuperClass{

static {
System.out.println("子类进行初始化");
}

}
public class MainTest {
public static void main(String args[]){
System.out.println(SubClass.getString());
}
}

执行结果:

父类初始化
10

2 在对一个类的常量,也就是有final修饰的变量,进行引用时,并不会对此类进行初始化.常量在编译阶段会存入调用它的类常量池中,本质上没有直接引用到定义该常量的类,因此不会触发定义常量的类的初始化.如下代码:

public class SuperClass {

public final static String NAME="name";

static{
System.out.println("父类初始化");
}

public static int s =1;

public static String getString(){
return "10";
}

}


public class MainTest {
public static void main(String args[]){
// System.out.println(SubClass.getString());
System.out.println(SuperClass.NAME);
}
}

执行结果:

name

SuperClass 类没有进行初始化,这一点和引用接口中定义的常量是不同的.下面会有讲解.

3.通过数组对一个类进行引用时.

public class MainTest {
public static void main(String args[]){
// System.out.println(SubClass.getString());
// System.out.println(SuperClass.NAME);
SuperClass[] supe = new SuperClass[10];
}
}

这种情况下并不会执行对SuperClass的初始化.

三、接口的初始化
虚拟机对接口的初始化和对类的初始化是有区别的:
类初始化时,需要完成其所有父类的初始化工作,而对于它实现的接口,虚拟机并不要求其父接口全部初始化完成.

只有真正使用到父接口的时候(比如引用接口中的常量,调用接口中定义的方法)才会初始化该接口.
多说一句,使用接口的常量,其实只能在它的具体实现的子类中引用,因为在子接口中是无法使用的. 如下两个父子接口,两个常量s之间并不存在继承关系,两个s是完全不同的.


public interface SuperInterface {

int s = 0;

public void getString();
}
public interface SubInterface extends SuperInterface{

int s=1;

}
public class SubClass implements SubInterface{


@Override
public void getString() {
System.out.println(SubInterface.s);
System.out.println(SuperInterface.s);

}

public static void main(String args[]){
SubClass s = new SubClass();
s.getString();

}

}

执行结果:

SubInterface.s:1
SuperInterface.s:0

接口中定义的常量,也就是由final static 修饰的,这个和对类中定义的常量引用时,进行的初始化是不一样的.