Java虚拟机ClassLoader入门

        最近有强烈的欲望看《深入Java虚拟机》,无奈图书馆一直不开门。偶然间看到网上流传的张龙老师的JVM视频教程,就下载下来准备看看。这一看简直停不下来了,张龙讲得非常好。一口气看完了七讲视频,这里向大家推荐了。这里我把张龙老师的开篇例子和大家分享一下。

        代码片段一:

class Singleton {
	private static Singleton singleton = new Singleton();
	
	public static int counter1;
	
	public static int counter2 = 0;
	
	private Singleton() {
		counter1++;
		counter2++;
	}
	
	public static Singleton getInstance() {
		return singleton;
	}
}
public class MyTest {
	
	public static void main(String[] args) {
		
		Singleton singleton = Singleton.getInstance();
		System.out.println("counter1 = " + singleton.counter1);
		System.out.println("counter2 = " + singleton.counter2);
		
	}
}
        代码片段二:
class Singleton {

	public static int counter1;
	
	public static int counter2 = 0;
	
	private static Singleton singleton = new Singleton();
	
	private Singleton() {
		counter1++;
		counter2++;
	}
	
	public static Singleton getInstance() {
		return singleton;
	}
}
public class MyTest {
	
	public static void main(String[] args) {
		
		Singleton singleton = Singleton.getInstance();
		System.out.println("counter1 = " + singleton.counter1);
		System.out.println("counter2 = " + singleton.counter2);
		
	}
}
        这两段代码的执行结果应该是什么呢?大家可以自己运行一下试试,我这里就不卖关子了。代码一的执行结果是counter1 = 1, counter2 = 0,而代码二的执行结果是counter1 = 1, counter2 = 1。为什么会这样呢?我当时也是一头雾水。其实这种奇怪现象的“始作俑者”就是ClassLoader(类加载器)。首先明确一个基本问题,就是Java中的类是需要内存空间的。这是显然的了,类的方法、类的静态变量都是存放在内存中的(当然还有一个对应于该类的Class对象)。Java中的类是如此的多,我们不可能把它们一起加载到内存里。JVM的策略是当代码“直接使用”某个类时,就使用ClassLoader把它加载到内存里。直接使用的情况有如下六种:

                -创建类的实例

                -访问某个类或接口的静态变量,或者是对该静态变量赋值

                -调用类的静态方法

                -反射 (如Class.forName("com.shengsiyuan.Test"))

                -初始化一个类的子类

                -Java虚拟机启动时被表明为启动的类

         除了以上六种情况,其他使用Java类的方式都被看作是对类的被动使用,都不会导致类的初始化。其他几种情况我们先不要管,前面的例子中“直接访问”的情况是调用了Singleton类的静态方法getInstance(),这样的调用导致Singleton类被ClassLoader加载到了内存里。加载之后并没有完事,还有连接和初始化两个步骤。如下图内容所讲:

        验证和解析过程我们就不去研究了,在这个例子中他们没有什么实质性的作用。准备这个过程为类的静态变量分配内存,然后将其初始化为默认值。对于上面的两个例子来说,就是将counter1和counter2都初始化为0,将singleton初始化为null(非基本类型的默认值就是null)。真正出现区别的时间是在初始化过程,这个初始化过程是按变量的声明顺序来的,从上到下依次执行。对于代码片段一,首先new一个Singleton对象,其中调用了构造函数,这行代码执行完毕之后,counter1 = 1, counter2 = 1;紧接着的一行中,没有给counter1指定初始值,所以什么都没干;最后第三行代码中,counter2被赋值为0。所以,代码一最后的结果是counter1 = 1, counter2 = 0。按照同样的方法分析,可以得到代码二的执行结果是counter1 = 1, counter2 = 1。

你可能感兴趣的:(Java)