看到你的问题将你的问题进行了还原,当然运行结果和你一样,为什么会是这样的结果呢?代码中,main方法在输出count1和count2的值之前只是调用了SingleTon的一个静态方法,而静态方法中也只是返回了SingleTon的一个对象,JVM在这个过程中做了哪些事情呢?

首先必须确认,调用一个类的静态方法会造成这个类的初始化,而JVM将整个类加载过程划分成了三个步骤:

(1)装载。装载过程负责找到二进制字节码(.class文件)并加载到JVM中,JVM通过类名、类所在的包名、类加载器(ClassLoader)来完成类的加载,同样,也采用以上三个元素来标识一个被加载了的类:类名+包名+ClassLoader实例ID。这里的.class文件可以从本地系统中直接加载,也可以从jar等归档文件中加载,还可以是从网络上下载的等方式。

(2)链接。链接过程负责对二进制字节码的格式进行校验、初始化装载类中的静态变量以及解析类中调用的接口、类。校验是为了确保加载的类的正确性。在完成了校验后,JVM为类中的静态变量分配合理的内存,并将其赋值为默认值。最后一步为对类中的所有属性、方法进行验证,以确保其需要调用的属性、方法存在,以及具备应有的权限(例如public、private等权限),会造成NoSuchMethodError、NoSuchFieldError等错误信息。

(3)初始化。初始化的目的是为静态属性赋予正确的初始值,过程即执行静态属性初始化、静态代码块,在以下几种情况下会造成一个类被初始化(即静态内容得到初始化):通过new创建了类的实例、访问了某个类的静态变量或调用了静态方法、反射、初始化一个类的子类、JVM启动过程中指定的初始化类(如main所在的类)。

来分析下问题,这里通过静态方法获取到了SingleTon的一个实例,分别对照上面三步:

第一步,JVM从本地系统中找到SingleTon.class并加载入虚拟机,这个好说,任何一个java的类一定是先编译生成class文件才能使用的。

第二步,JVM确保SingleTon.class是正确的后,会对SingleTon的静态属性分配内存并初始化为默认值,这里SingleTon有三个静态属性,根据它们的数据类型他们的默认值分别是null、0、0。

第三步,虚拟机通过等号和静态代码块为静态属性赋予正确的初始值。这个过程是按照从上到下的顺序完成的,即依次为属性singleTon、count1、count2赋值,这里没有静态代码块,我们来看下等号赋值,为songleTon赋的值是一个SingleTon对象,会通过非静态代码块和构造方法为这个对象进行初始化,这里通过执行构造方法将count1和count2的值分别修改为了1,校验如下:

但是到这里,静态属性赋值还没完,singleTon后是count1,代码中没有使用=为count1赋值,所以count1维持之前的值1,最后是count2,代码中通过等号为count2赋值为了0,所以count2的值会由1变为0,这就导致最后得到的值为count1=1,count2=0。