首先明确一点,类初始化加载static修饰的属性/代码块的时候是按照从上到下加载的,
实例:
package com.wm.jasypt.service;
/**
* @author 半卷流年
* @date 2020-6-3 15:56
*/
public class Singleton {
private static Singleton singleton = new Singleton();
public static int counter1;
public static int counter2 = 0;
private Singleton() {
counter1++;
counter2++;
}
public static Singleton getSingleton() {
return singleton;
}
public static void main(String[] args) {
Singleton singleton = Singleton.getSingleton();
System.out.println("counter1="+singleton.counter1); //输出什么?
System.out.println("counter2="+singleton.counter2); //输出什么?
}
}
输出的结果如下:
counter1=1
counter2=0
原因分析:
1.执行main方法的时候,由于Singleton 类没有被加载,首先触发类加载器,初始化该类,注意类的初始化是只会初始化一次的
singleton = null;
counter1 = 0;
counter2 = 0;
2.按照static顺序加载,先进入new Singleton()初始化,进入构造函数的加载, 执行完成之后
counter1 = 1;
counter2 = 1;
3.在执行 public static int counter1;
由于没有指定初始值,故 counter1位之前初始化的值:
counter1 = 1;
4.执行 public static int counter2 = 0;
所以counter2的值为:
counter2 = 0;
综合得出结果: counter1 = 1; counter2 = 0;
下面debug如下: 依次的顺序如下:
1.
2.
3.
下面将上面的例子更换一个顺序,输出的是什么:
package com.wm.jasypt.service;
/**
* @author 半卷流年
* @date 2020-6-3 15:56
*/
public class Singleton {
public static int counter1;
public static int counter2 = 0;
private static Singleton singleton = new Singleton();
private Singleton() {
counter1++;
counter2++;
}
public static Singleton getSingleton() {
return singleton;
}
public static void main(String[] args) {
Singleton singleton = Singleton.getSingleton();
System.out.println("counter1="+singleton.counter1); //输出什么?
System.out.println("counter2="+singleton.counter2); //输出什么?
}
}
结果为:
counter1=1
counter2=1
原因:
是先初始化counter1,2赋值,在进入构造,故输出都是1
类加载机制,主动初始化化的几种情况:
主动初始化的6种方式
(1)创建对象的实例:我们new对象的时候,会引发类的初始化,前提是这个类没有被初始化。
(2)调用类的静态属性或者为静态属性赋值
(3)调用类的静态方法
(4)通过class文件反射创建对象
(5)初始化一个类的子类:使用子类的时候先初始化父类
(6)java虚拟机启动时被标记为启动类的类:就是我们的main方法所在的类
只有上面6种情况才是主动使用,也只有上面六种情况的发生才会引发类的初始化。
关于类加载器的双亲委派机制:
关于类加载器,我们不得不说一下双亲委派机制。听着很高大上,其实很简单。比如A类的加载器是AppClassLoader(其实我们自己写的类的加载器都是AppClassLoader),AppClassLoader不会自己去加载类,而会委ExtClassLoader进行加载,那么到了ExtClassLoader类加载器的时候,它也不会自己去加载,而是委托BootStrap类加载器进行加载,就这样一层一层往上委托,如果Bootstrap类加载器无法进行加载的话,再一层层往下走。
为什么使用双亲委派机制:
判断两个类相同的前提是这两个类都是同一个加载器进行加载的,如果使用不同的类加载器进行加载同一个类,也会有不同的结果。
如果没有双亲委派机制,会出现什么样的结果呢?比如我们在rt.jar中随便找一个类,如java.util.HashMap,那么我们同样也可以写一个一样的类,也叫java.util.HashMap存放在我们自己的路径下(ClassPath).那样这两个相同的类采用的是不同的类加载器,系统中就会出现两个不同的HashMap类,这样引用程序就会出现一片混乱。