单例模式,以及各种实现方式优缺点
恶汉模式
// 恶汉模式
// 在类加载期间初始化静态实例,保证实例的创建是线程安全的.
// 优点: 不支持延迟加载实例(懒加载) , 此中方式类加载比较慢,但是获取实例对象比较快
// 缺点: 该对象足够大的话,而一直没有使用就会造成内存的浪费
public class Singleton_01 {
// 私有方法,无法通过new方式获取
private Singleton_01(){
}
// static 保证类加载时候创建对象
// final 保证调用方不会引用其他对象
private static final Singleton_01 instance = new Singleton_01();
// 公共的访问方式,供外部访问 获取到单例对象
public static Singleton_01 getInstance(){
return instance;
}
}
// 懒汉式(线程安全)
// 同步静态方法获取单例对象,synchronized保证多线程唯一性以及可见性
// 优点:推迟对象初始化时间点,在需要时候创建单例对象,相比恶汉模式,减少类加载性能开销。同时同步方法保证了多线程场景下单例对象唯一性
// 缺点:每次获取单例对象时会上锁增加性能开销,无论是否已经初始化instance
public class Singleton_03 {
private Singleton_03(){}
// static 获取单例对象方法为static修饰
private static Singleton_03 instance;
// 方法上添加synchronized,锁对象为当前类class对象
public static synchronized Singleton_03 getInstance(){
if(instance == null){
instance = new Singleton_03();
}
return instance;
}
}
双重校验
// 双重校验
// 优点:推迟对象初始化时间点,在需要时候创建单例对象,相比恶汉模式,减少类加载性能开销。相比 懒汉式(线程安全) 不用每次都获取锁对象减少无意义锁获取造成的性能开销
// 缺点:有序性问题,在调用new创建单例对象时候实际过程为1 .
public class Singleton_04 implements Serializable {
private Singleton_04(){}
// volatile: 每次获取单例对象时候检查是否已初始化,因此需要 volatile 修饰
// static: 获取单例对象方法为静态方法,静态上下文使用的属性需要用static修饰
private static volatile Singleton_04 instance = null;
public static Singleton_04 getInstance(){
if(instance == null){
synchronized(Singleton_04.class){
if(instance == null){
instance = new Singleton_04();
}
}
}
return instance;
}
// 反射和序列化破坏单例模式
public static void main(String[] args) throws Exception {
serialDestroySingleton();
}
// 反射破坏单例模式
public static void refelectionDestroySingleton() throws Exception{
Class<Singleton_04> clazz = Singleton_04.class;
Constructor<Singleton_04> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton_04 obj = constructor.newInstance();
Singleton_04 obj2 = getInstance();
System.out.println(obj2 == obj); // false
}
// 序列化破坏单例模式
public static void serialDestroySingleton() throws Exception{
Singleton_04 obj2 = getInstance();
ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("singleton.obj"));
output.writeObject(obj2);
// 将序列化对象反序列化,创建对象
ObjectInputStream input = new ObjectInputStream(new FileInputStream(new File("singleton.obj")));
Singleton_04 obj = (Singleton_04)input.readObject();
System.out.println(obj == obj2 );// false
}
}
静态内部类实现
// 静态内部类
// 优点:同样做到单例对象延迟初始化,由于是通过类加载方式初始化单例对象,所以没有线程安全问题
// 缺点:通过序列化和反序列化方式可以破坏单例模式
public class Singleton_05 implements Serializable {
private static class SingletonHolder{
private static Singleton_05 instance = new Singleton_05();
}
private Singleton_05(){
// 放置通过反射方式多次创建不同单例对象
if(SingletonHolder.instance != null){
throw new RuntimeException("不允许非法访问!");
}
}
public static Singleton_05 getInstance(){
return SingletonHolder.instance;
}
// 反射方式破坏单例模式
public static void fun() throws Exception{
Class<Singleton_05> clazz = Singleton_05.class;
Constructor<Singleton_05> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
// 多次创建对象,并且两个单例对象是不同的
Singleton_05 instance1 = constructor.newInstance();
Singleton_05 instance2 = constructor.newInstance();
System.out.println(instance2 == instance1); // false
}
// 序列化对单例模式的破坏
public static void fun2() throws Exception{
// 将内存中单例对象序列化
ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("singleton.obj"));
output.writeObject(getInstance());
// 将序列化对象反序列化,创建对象
ObjectInputStream input = new ObjectInputStream(new FileInputStream(new File("singleton.obj")));
Singleton_05 obj = (Singleton_05)input.readObject();
// 比较两个对象是否相等
System.out.println("compare 2 obj");
System.out.println(obj == obj.getInstance()); // false
}
public static void main(String[] args) {
try {
fun2();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
枚举实现方式
// 枚举方式
// 优点:由于枚举没有无参构造方法,所以无法通过反射调用构造方法创建实例 ,同时序列化也无法破坏单例模式
public enum Singleton_06 {
INSTANCE;
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static Singleton_06 getInstance(){
return INSTANCE;
}
}