类加载器
在正式写文章之前首先要弄清楚java中的类是如何加载到jvm中的.
类加载
不管怎么,类总是在第一次被用到的时候,动态的被加载到jvm中的,这里有两层意思:
- java程序不是在一开始运行时就全部加载了所以的class文件,只有在用到但还没有加载的时候才会取加载它。
- 当程序创建了第一个对类的静态成员的引用时(构造方法也属于静态方法),才会加载.
需要注意:类的加载和初始化的区别
类加载过程大概分为3个阶段: 加载---链接---初始化
- 加载:由类加载器执行,查找字节码,并创建一个Class对象(只是创建);
- 链接: 验证字节码,为静态域分配存储空间(只是分配,并不初始化该存储空间),解析该类创建所需要的对其它类的应用;
- 初始化:执行静态初始化代码块,初始化静态变量,执行静态方法(如构造方法)
链接
链接简单说就是把加载的class文件组合到jvm的运行状态中,包括三个步骤: 1.验证 2.准备: 这一步就就是创建静态域,分配空间,设置默认值,但是这部分不会执行代码,仅仅设置默认值 3. 解析:解析的过程就是对类中的接口、类、方法、变量的符号引用进行解析并定位,解析成直接引用(符号引用就是编码是用字符串表示某个变量、接口的位置,直接引用就是根据符号引用翻译出来的地址),并保证这些类被正确的找到。解析的过程可能导致其它的类被加载。需要注意的是,根据不同的解析策略,这一步不一定是必须的,有些解析策略在解析时递归的把所有引用解析,这是early resolution,要求所有引用都必须存在;还有一种策略是late resolution,这也是Oracle 的JDK所采取的策略,即在类只是被引用了,还没有被真正用到时,并不进行解析,只有当真正用到了,才去加载和解析这个类.(引用自其他博文)
初始化
这里进行的是类的初始化,总的来说一句话,static代码块在类初始化的时候执行且只执行一次。 注意
public class StaticTest {
public static void main(String[] args){
// staticFunction();
}
static StaticTest st = new StaticTest();
static
{
System.out.println("1 ");
}
{
System.out.println("2");
}
StaticTest()
{
System.out.println("3");
System.out.println("a="+a+",b="+b);
}
public static final void staticFunction(){
final int f=10;
System.out.println("4 + "+f);
}
int a=110;
static int b =112;
}
这是面试时遇上一各特殊的类初始化题,运行输出为:
2
3
a=110l , b=null
1
4
可以这样解释,在该类初始化过程中 执行到 static StaticTest st = new StaticTest();这时需要实例化此类,这时候类初始化暂停(此时正在进行),转身实例化该类,但是还未初始化完成,这时实例化过程将直接进行,而不再次进行类的初始化。这样也符合类初始化时执行且只执行一次的策略。
可以这样认为:当类初始化过程开始进行时,过程中如果有其他线程需要引用该类,但类初始化过程不会在第一次执行初始化过程的线程之外执行。
未完待续