在这段代码中,我们使用了一个简单的单例模式,定义了两个int类型的静态变量,其中x我们为它赋值位0,y则不做任何赋值,在Singleton的构造方法中分别对x,y ++,main方法中分别打印x,y值,可能大家已经猜到了结果,那么我们来验证下,
public class Singleton { private static Singleton instance = new Singleton(); public Singleton() { x++; y++; } public static int x = 0; public static int y; public static Singleton getInstance() { return instance; } public static void main(String[] args) { Singleton instance = getInstance(); System.out.println(instance.x); System.out.println(instance.y); } }
同学们你的答案猜对了吗?
为什么会是0,1??
下面我们将
private static Singleton instance = new Singleton();
这段代码移动到下x,y值后面,输出结果又会是什么呢?小伙伴们不妨在猜下,
public class Singleton { public Singleton() { x++; y++; } public static int x = 0; public static int y; private static Singleton instance = new Singleton(); public static Singleton getInstance() { return instance; } public static void main(String[] args) { Singleton instance = getInstance(); System.out.println(instance.x); System.out.println(instance.y); } }
要解释这个不同的输出结果,我们首先要了解类加载机制的三个步骤,
- 加载
- 链接
2.1.验证
2.2. 准备
2.3. 解析
- 初始化
首先类按照顺序加载,比如说这段代码
private static Singleton instance = new Singleton();
如果在类中的第一行,就第一个被加载,
然后是连接阶段,分为三个阶段
1.验证
文件格式的验证:魔术因子,版本号,字节流,常量池中的数据,常量的引用等等
元数据的验证:是否被final修饰,是不是抽象类,是不是有父类,子类这些,重载的合法性,
字节码验证:保持当前指针不会被其他类引用,程序计数器中的指令,不会跳转到不合法的字节码中
符号验证:比如说引用类中的字段,方法,是否对当前的类课件等等,
2.准备,当验证通过之后,便开始为类的静态变量赋初始值,
3.解析,所谓解析就是指,刚刚验证,并且初始化的常量寻找这些常量的类,接口,方法的符号引用,并且将这些符号引用转换成直接引用
最好初始化静态变量的值
在第一个代码片段中,首先加载的是构造方法,然后才是x,y,接着就是调用构造方法,然后是获取实例的方法,根据类加载的三个步骤,
首先是加载,类加载的最终的目的就是将java字节码数据,加载到jvm内存中,比如说大家都知道jvm的内存结构,最常用的两块就是堆内存和栈内存,那么加载阶段就是将这些数据加载到jvm内存中;
其次是链接,链接的过程上面基本已经交代了,需要注意的是,在链接阶段的准备和解析的过程中,x=0,这个0并不是=(等于的),而是x的默认值,需要在初始化阶段这个等号才会起作用,将0,赋值给x,所以当new Singleton()方法执行x++后,执行到了x=0,又将原来的x=1 赋值为了x=0
但是当我们将new Singleton()方法移动到x,y之后,为什么又变成了两个1呢,请小伙伴们思考思考,如果有答案了,可以@我,或者评论,其实文章中已经详细的解释了,相信各位小伙伴可以找到答案。