单例的创建方式:
1、饿汉式:类初始化的时候,会立即加载该对象,线程天生安全,调用效率高。
2、懒汉式:类初始化时,不会初始化该对象,真正需要使用的时候才会去创建该对象,具备懒加载功能。
3、静态内部类方式:结合了懒汉式和饿汉式各自的优点,真正需要对象的时候才会加载,加载类是线程安全的。
4、枚举单例:使用枚举实现单例模式,实现简单、调用效率高,枚举本身就是单例,由JVM从根本上提供保障,避免通过反射和反序列化的漏洞,缺点是没有延迟加载。
5、双重检测方式(因为JVM本身重排序的原因,可能会出现多次的初始化)
示例:
1、饿汉式
1 //饿汉式 2 public class SingletonDemo01 { 3 // 类初始化时,会立即加载该对象,线程天生安全,调用效率高 4 private static SingletonDemo01 singletonDemo01 = new SingletonDemo01(); 5 6 private SingletonDemo01() { 7 System.out.println("SingletonDemo01初始化"); 8 } 9 10 public static SingletonDemo01 getInstance() { 11 System.out.println("getInstance"); 12 return singletonDemo01; 13 } 14 15 public static void main(String[] args) { 16 SingletonDemo01 s1 = SingletonDemo01.getInstance(); 17 SingletonDemo01 s2 = SingletonDemo01.getInstance(); 18 System.out.println(s1 == s2); 19 } 20 21 }
2、懒汉式
1 //懒汉式 2 public class SingletonDemo02 { 3 4 //类初始化时,不会初始化该对象,真正需要使用的时候才会创建该对象。 5 private static SingletonDemo02 singletonDemo02; 6 7 private SingletonDemo02() { 8 9 } 10 11 public synchronized static SingletonDemo02 getInstance() { 12 if (singletonDemo02 == null) { 13 singletonDemo02 = new SingletonDemo02(); 14 } 15 return singletonDemo02; 16 } 17 18 public static void main(String[] args) { 19 SingletonDemo02 s1 = SingletonDemo02.getInstance(); 20 SingletonDemo02 s2 = SingletonDemo02.getInstance(); 21 System.out.println(s1 == s2); 22 } 23 24 }
3、静态内部类
1 public class SingletonDemo03 { 2 private SingletonDemo03() { 3 System.out.println("初始化.."); 4 } 5 6 public static class SingletonClassInstance { 7 private static final SingletonDemo03 singletonDemo03 = new SingletonDemo03(); 8 } 9 10 // 方法没有同步 11 public static SingletonDemo03 getInstance() { 12 System.out.println("getInstance"); 13 return SingletonClassInstance.singletonDemo03; 14 } 15 16 public static void main(String[] args) { 17 SingletonDemo03 s1 = SingletonDemo03.getInstance(); 18 SingletonDemo03 s2 = SingletonDemo03.getInstance(); 19 System.out.println(s1 == s2); 20 } 21 }
优势:兼顾了懒汉模式的内存优化(使用时才初始化)以及饿汉模式的安全性(不会被反射入侵)。
劣势:需要两个类去做到这一点,虽然不会创建静态内部类的对象,但是其 Class 对象还是会被创建,而且是属于永久带的对象。
4、枚举
1 public class User { 2 public static User getInstance() { 3 return SingletonDemo04.INSTANCE.getInstance(); 4 } 5 6 private static enum SingletonDemo04 { 7 INSTANCE; 8 // 枚举元素为单例 9 private User user; 10 11 private SingletonDemo04() { 12 .out.println("SingletonDemo04"); 13 user = new User(); 14 } 15 16 public User getInstance() { 17 return user; 18 } 19 } 20 21 public static void main(String[] args) { 22 User u1 = User.getInstance(); 23 User u2 = User.getInstance(); 24 System.out.println(u1 == u2); 25 } 26 }
5、双重检测锁
1 public class SingletonDemo04 { 2 private SingletonDemo04 singletonDemo04; 3 4 private SingletonDemo04() { 5 6 } 7 8 public SingletonDemo04 getInstance() { 9 if (singletonDemo04 == null) { 10 synchronized (this) { 11 if (singletonDemo04 == null) { 12 singletonDemo04 = new SingletonDemo04(); 13 } 14 } 15 } 16 return singletonDemo04; 17 } 18 19 }
单例防止反射攻击,在构造函数中,只能允许初始化一次即可
1 private static boolean flag = false; 2 3 private SingletonDemo04() { 4 5 if (flag == false) { 6 flag = !flag; 7 } else { 8 throw new RuntimeException("单例模式被侵犯!"); 9 } 10 }