接着就上面步骤完成后,就会进入准备阶段了:
这阶段会为类变量(指那些静态变量)分配内存并设置类比那辆初始值的阶段,这些内存在方法区中进行分配。这里要说明一下,这一步只会给那些静态变量设置一个初始的值,而那些实例变量是在实例化对象时进行分配的。这里的给类变量设初始值跟类变量的赋值有点不同,比如下面:
public static int value=123;
在这一阶段,value的值将会是0,而不是123,因为这个时候还没开始执行任何java代码,123还是不可见的,而我们所看到的把123赋值给value的putstatic指令是程序被编译后存在于
这里也有个例外:
public static final int value=123;
这里在准备阶段value的值就会初始化为123了。这个是说,在编译期,javac会为这个特殊的value生成一个ConstantValue属性,并在准备阶段jm就会根据这个ConstantValue的值来为value赋值了。
完成上步后,就要进行解析了。解析好像是对类的字段,方法等东西进行转换,具体涉及到Class文件的格式内容,并没深入去了解。
初始化过程是类加载过程的最后一步:
在前面的类加载过程中,除了在加载阶段用户可以通过自定义类加载器参与之外,其他的动作完全有jvm主导,到了初始化这块,才开始真正执行java里面的代码。
这一步将会执行一些预操作,注意区分在准备阶段,已经为类变量执行过一次系统赋值了。
其实说白了,这一步就是执行程序的
下面来个例子说明一下:
[java]
static class Parent{
public static int A=1;
static{
A=2;
}
}
static class Sub extends Parent{
public static int B=A;
}
public static void main(String[] args){
System.out.println(Sub.B);
}
首先Sub.B中对静态数据进行了引用,Sub类要进行初始化了。同时,其父类Parent要先进行初始化动作。Parent初始化后,A=2,所以B=2;上个过程相当于:
[java]
static class Parent{
public static int A=1;
static{
A=2;
}
}
}
static class Sub extends Parent{
public static int B=A;
}
}
public static void main(String[] args){
System.out.println(Sub.B);
}
由于接口里面不能存在static{}这种静态代码块,但仍然可能存在变量初始化时的变量赋值操作,所以接口里面也会生成
另外,接口的实现类在初始化的时候也一样不会执行接口的
另外,jvm会保证一个类的
下面用个例子说明一下:
[java]
public class DeadLoopClass {
static{
if(true){
System.out.println("要被 ["+Thread.currentThread()+"] 初始化了,下面来一个无限循环");
while(true){}
}
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("toplaile");
Runnable run=new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("["+Thread.currentThread()+"] 要去实例化那个类了");
DeadLoopClass d=new DeadLoopClass();
System.out.println("["+Thread.currentThread()+"] 完成了那个类的初始化工作");
}}; www.2cto.com
new Thread(run).start();
new Thread(run).start();
}
}
这里面,运行的时候将会看到阻塞现象。
呼呼~先到这里`