单例设计模式与类加载顺序详解

单例设计模式几种实现 (测试所用jdk版本1.8.0_20)

第一种:

 1 public final class SingleInstance1 {
 2 
 3     private static volatile SingleInstance1 singleInstance = null;
 4     /*
 5      * 必须定义,防止jvm提供默认构造函数(默认的为public)这样就失去了单例的特性,
 6      * 因为可以new一个实例
 7      */
 8     private SingleInstance1(){}
 9     public static SingleInstance1 getInstance(){
10         if(singleInstance == null){
11             synchronized (SingleInstance1.class) {
12                 if(singleInstance == null){
13                     singleInstance = new SingleInstance1();
14                 }
15             }
16         }
17         return singleInstance;
18     }
19 }
View Code

这种模式是单锁,有线程安全问题 所以用两把锁

第二种:

 1 /**
 2  * 
 3  *  说明:线程安全的单例
 4  *
 5  */
 6 public final class SingleInstance2 {
 7 
 8     private static volatile SingleInstance2 singleInstance = null;
 9 
10     /*
11      * 必须定义,防止jvm提供默认构造函数(默认的为public)这样就失去了单例的特性, 因为可以new一个实例
12      */
13     private SingleInstance2() {
14     }
15 
16     public static SingleInstance2 getInstance() {
17         if (singleInstance == null) {
18             synchronized (SingleInstance2.class){
19                 SingleInstance2 tmpInstance2 = singleInstance;
20                 synchronized (SingleInstance2.class) {
21                     if (tmpInstance2 == null) {
22                         tmpInstance2 = new SingleInstance2();
23                     }
24                     singleInstance = tmpInstance2;
25                 }
26             }
27         }
28         return singleInstance;
29     }
30 }
View Code

第三种:

 1 /**
 2  * 
 3  * 说明:饿汉式 不是线程安全的
 4  *
 5  */
 6 public class SingleInstance3 {
 7     private static SingleInstance3 singleInstance = null;
 8     
 9     private SingleInstance3(){}
10     
11     public SingleInstance3 getInstance3() {
12         if(singleInstance == null) {
13             singleInstance = new SingleInstance3();
14         }
15         return singleInstance;
16         
17     }
18 
19 }
View Code

第四种:

 1 /**
 2  * 
 3  * 说明:懒汉式  缺点:每个对象在没有使用之前就已经初始化了。这就可能带来潜在的性能问题
 4  * 没有使用这个对象之前,就把它加载到了内存中去是一种巨大的浪费
 5  */
 6 public class SingleInstance4 {
 7     private static SingleInstance4 singleInstance = new SingleInstance4();
 8 
 9     private SingleInstance4() {
10     }
11 
12     public SingleInstance4 getInstance() {
13         return singleInstance;
14     }
15 
16 }
View Code

第五种:

 1 /**
 2  * 
 3  *说明:线程安全 但是效率低,因为只需要创建时同步就可以了 不需要每次访问都要同步
 4  */
 5 public class SingleInstance5 {
 6     private static SingleInstance5 singleInstance5 = null;
 7     private SingleInstance5() {
 8     }
 9 
10     public static synchronized SingleInstance5 getInstance5(){
11         if(singleInstance5 == null){
12             singleInstance5 = new SingleInstance5();
13         }
14         return singleInstance5;
15     }
16 }
View Code

第六种:

 1 /**
 2  *
 3  *内部类静态属性(或静态块)会在内部类第一次被调用的时候按顺序被初始化(或执行)而且只会被加载一次
 4  *测试请看StaticInnerClassLoaderTime类
 5  */
 6 public class SingleInstance6 {
 7     private SingleInstance6() {}
 8     private static class SingletonHolder {
 9         public final static SingleInstance6 instance = new SingleInstance6();
10     }    
11 
12     public static SingleInstance6 getInstance() {
13         return SingletonHolder.instance;
14     }
15 
16 }
View Code

StaticInnerClassLoaderTime类

 1 public class StaticInnerClassLoaderTime {
 2     
 3     public static class Inner {
 4         static {
 5             System.out.println("TestInner Static!");
 6         }
 7         public final static StaticInnerClassLoaderTime testInstance = 
 8                 new StaticInnerClassLoaderTime(3);
 9     }
10 
11     public static StaticInnerClassLoaderTime getInstance() {
12         return Inner.testInstance;
13     }
14 
15     public StaticInnerClassLoaderTime(int i) {
16         System.out.println("Test " + i + " Construct! ");
17     }
18     
19     
20     // 类静态属性
21     public static StaticInnerClassLoaderTime testOut =
22             new StaticInnerClassLoaderTime(1);
23 
24     public static int value = 3;
25     // 类静态块
26     static {
27         System.out.println("Test Static" + testOut);
28     }
29 
30     public static void main(String args[]) {
31         StaticInnerClassLoaderTime t = new StaticInnerClassLoaderTime(2);
32         //说明静态内部类只有在调用时才会被加载 而且只会被加载一次 因此最好的单例设计模式是用静态内部类
33         StaticInnerClassLoaderTime.getInstance();
34         StaticInnerClassLoaderTime.getInstance();
35     }
36 
37 }
View Code

运行结果:

1 Test 1 Construct!
2 Test Staticcn.canon.Single.StaticInnerClassLoaderTime@15db9742
3 Test 2 Construct!
4 TestInner Static!
5 Test 3 Construct! 
View Code

说明:

 顺序是:父类静态属性-》父类静态代码块-》子类静态变量-》子类静态代码块-》父类非静态变量-》父类非静态代码块-》父类构造函数-》
 子类非静态变量-》子类非静态代码块-》-》子类构造函数

这样的加载顺序不是绝对的 因为静态变量和静态代码块跟声明顺序有关。

对于如果静态代码块中调用静态变量,那么静态变量必须在静态代码块前面声明;如果静态代码块中没有调用静态变量,
那么就跟顺序有关了,谁先声明谁先被加载。说白了还是顺序加载,之所以会出现“如果静态代码块中调用静态变量,
那么静态变量必须在静态代码块前面声明”,是因为变量是声明,所以出现编译错误。

应用到内部类中 静态变量和静态代码块跟声明顺序有关。 这样就可以解释你的问题了。内部类也是类。

类静态块-类静态属性这个跟顺序有关系 如果类静态属性在类静态代码块之前 那么类静态属性先初始化

原文地址:https://www.cnblogs.com/yang--yang/p/4856957.html