设计模式02——单例模式、原型模式

122 阅读4分钟

核心作用:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点

优点:当生成一个对象的需要比较多的资源时,只生成一个实例减少了系统性能开销,如读取配置时。

单例模式可以设置系统全局访问点优化共享资源访问

实现的五种方式

一主要︰

-饿汉式(线程安全,调用效率高。但是,不能延时加载。)

-懒汉式(线程安全,调用效率不高。但是,可以延时加载。)

一其他(补充,面试加分)︰

-静态内部类式(线程安全,调用效率高。同时,可以延时加载)

-枚举单例(线程安全,调用效率高,不能延时加载)

-双重检测锁式(由于指令重排所以不安全)·

饿汉式实现

实现方式

在类的工厂中设置一个类自身的静态引用并立刻创建实例,构造器私有化,公开一个静态方法获取实例。

线程安全来自于加载类时立刻创建对象,类加载是天然线程安全的,不需要加同步块所以方法调用效率高

 public class Singleton {
     private Singleton(){}
     private static Singleton instance = new Singleton();
     public static Singleton getInstance() {
         return instance;
     }
 }

懒汉式实现

与饿汉式的区别,加载类是并不直接初始化对象,真正调用该方法的时候才生成对象,有同步块所以调用效率低。

 public class Singleton2 {//懒汉式
     private static Singleton2 instance=null;
     private Singleton2(){}
     //方法同步,调用效率低
     public synchronized static Singleton2 getInstance(){//延迟加载
         if(instance==null){
             instance=new Singleton2();
         }
         return instance;
     }
 }

双重检测锁式实现

双重检测锁式是懒汉式的一种,方法同步的代价太高,将synchronized关键字放入到方法中,判断无对象时才加锁并创建对象。

 public class Singleton {
     private static volatile Singleton instance=null;//注意volatile防止指令重排!!!
     private Singleton(){//私有构造方法
     }
     public static Singleton getInstance(){
         if(instance==null){
             synchronized (Singleton.class){//同步
                 if(instance==null){
                     instance=new Singleton();//创建对象
                 }
             }
         }
         return instance;
     }
 }

静态内部类实现

静态内部类实现(一种懒汉加载方式)

同时具备线程安全懒加载调用效率高

 //加载外部类的时候不会立刻加载它的静态内部类,由此实现懒加载
 public class Singleton {
     //加载外部类的时候不会立刻加载它的静态内部类,由此实现懒加载
     //第一次使用静态内部类中的静态成员时才会加载并静态内部类,且初始化其中的静态成员,所以天然线程安全
     private static class SingletonClassInstance{
         private static final Singleton instance=new Singleton();
     }
     private Singleton() {
     }
     //方法不同步,调用效率高
     public static Singleton getInstance(){
         return SingletonClassInstance.instance;
     }
 }

image-20220310212721718

反射与反序列化漏洞

反射破解单例模式:通过反射可以跳过权限检测使用私有构造器构造实例。

防止反射破解单例:在私有构造器中,如下:

 private Singleton() {//私有构造器   
     if(instance!=null){ //防止反射破解,若存在实例后再次调用构造器则抛出异常
         throw new RuntimeException();
     //return;
     }
 }

反序列化破解单例模式:反序列化时不需要调用私有构造器,直接通过对象流反序列化出新实例。

防止反序列化破解单例:添加一个readResolve() 方法,直接返回已经存在的实例对象

 //设置反序列化时直接调用该方法将存在的对象返回
 private Object readResolve() throws ObjectStreamException{
     return instance;
 }

原型模式

通过clone()方法创建新对象,若new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。New与clone()不同,new为一行行创建新对象,赋值为默认值,clone()是将旧对象从内存中复制一份。

浅克隆

浅克隆指将被克隆对象的所有值复制给新对象,包括被克隆对象成员类的引用,即新对象与旧对象中的成员类引用指向同一个对象。

实现方式:实现Cloneable接口,实例调用clone方法进行浅克隆。

深克隆

在浅克隆的基础上,新对象与被克隆对象的成员类引用不同,深克隆会将成员类对象也克隆出来赋给新对象的成员类引用。

实现方式1(继承Cloneable接口):在浅克隆的基础上,深克隆会重写clone()方法把属性也使用clone()方法复制一份,要修成员类也实现Cloneable接口。

实现方式2(序列化):将被克隆对象进行序列化,再反序列化则可得到深克隆对象,求成员属性与成员类不被transient关键字修饰。