设计模式学习(二)

设计模式学习(二)

一、单例模式

单例模式分为以下8种:

  1. 饿汉式:
    1. 静态常量
    2. 静态代码块
  2. 懒汉式:
    1. 线程不安全
    2. 线程安全,同步方法
    3. 线程安全,同步代码块
  3. 双重检查
  4. 静态内部类
  5. 枚举

单例模式的使用场景:

需要频繁创建和销毁的对象;创建时耗时过多或消耗资源过多,但又经常用到的对象(比如session工厂、数据源等)

(1)饿汉式 - 静态常量写法

代码实现:

/**
 * 设计模式之单例模式
 * 饿汉式(静态常量)
 *
 * @author song
 * @create 2020/3/4 15:08
 */
public class SingletonTest01 {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println("两次获取的实例一样吗:" + (instance1 == instance2)); //true
    }
}

class Singleton {

    //私有构造方法,使其不可在外部通过构造器实例化
    private Singleton() {

    }

    //定义为常量,保证实例对象不变
    private final static Singleton instance = new Singleton();


    //通过此方法获取实例
    public static Singleton getInstance() {
        return instance;
    }

}

分析:

优点:

  • 使用方式简单,在类加载的时候创建实例对象,避免了线程同步问题

缺点:

  • 在类加载的时候创建实例对象,但不确定何时使用、是否使用,可能造成内存浪费

(2)饿汉式 - 静态代码块写法

代码实现:

/**
 * 设计模式之单例模式
 * 饿汉式(静态代码块写法)
 *
 * @author song
 * @create 2020/3/4 15:08
 */
public class SingletonTest02 {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println("两次获取的实例一样吗:" + (instance1 == instance2)); //true
    }
}

class Singleton{

    //私有构造方法,使其不可在外部通过构造器实例化
    private Singleton(){

    }

    //定义为常量,保证实例对象不变
    private final static Singleton instance;

    static {
        instance = new Singleton();
    }

    //通过此方法获取实例
    public static Singleton getInstance(){
        return instance;
    }

}

分析:

和静态常量一致,只不过初始化的位置不同,一个在静态代码块,一个直接在常量声明处初始化

(3)懒汉式 - 线程不安全

代码实现:

/**
 * 设计模式之单例模式
 * 懒汉式(线程不安全)
 *
 * @author song
 * @create 2020/3/4 15:08
 */
public class SingletonTest03 {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println("两次获取的实例一样吗:" + (instance1 == instance2)); //true
    }
}

class Singleton {

    //私有构造方法,使其不可在外部通过构造器实例化
    private Singleton() {

    }

    //声明实例对象
    private static Singleton instance;

    //通过此方法获取实例
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

}

分析:

优点:

  • 满足随用随拿的特点,解决了内存浪费的问题

缺点:

  • 线程不安全,当多个线程访问时,可能创建多个实例,因此实际开发中不可使用

(4)懒汉式 - 线程安全 - 同步方法写法

代码实现:

/**
 * 设计模式之单例模式
 * 懒汉式(同步方法)
 *
 * @author song
 * @create 2020/3/4 15:08
 */
public class SingletonTest04 {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println("两次获取的实例一样吗:" + (instance1 == instance2)); //true
    }
}

class Singleton {

    //私有构造方法,使其不可在外部通过构造器实例化
    private Singleton() {

    }

    //声明实例对象
    private static Singleton instance;

    //通过此方法获取实例
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

}

分析:

虽然解决了线程不安全问题,但锁的范围太大,效率低,开发中尽量不要使用

(5)懒汉式 - 线程安全 - 同步代码块写法

代码实现:

/**
 * 设计模式之单例模式
 * 懒汉式(同步代码块写法)
 *
 * @author song
 * @create 2020/3/4 15:08
 */
public class SingletonTest05 {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println("两次获取的实例一样吗:" + (instance1 == instance2)); //true
    }
}

class Singleton {

    //私有构造方法,使其不可在外部通过构造器实例化
    private Singleton() {

    }

    //声明实例对象
    private static Singleton instance;

    //通过此方法获取实例
    public static Singleton getInstance() {
        if (instance == null) {
            //同步代码
            synchronized (Singleton.class) {
                instance = new Singleton();
            }
        }
        return instance;
    }

}

分析:

这种方式将同步锁缩小了范围,本意是解决效率问题,但又造成了线程不安全,因此开发中不可使用

(6)懒汉式 - 双重检查(推荐使用)

代码实现:

/**
 * 设计模式之单例模式
 * 双重检查
 *
 * @author song
 * @create 2020/3/4 15:08
 */
public class SingletonTest06 {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println("两次获取的实例一样吗:" + (instance1 == instance2)); //true
    }
}

class Singleton {

    //私有构造方法,使其不可在外部通过构造器实例化
    private Singleton() {

    }

    //声明实例对象
    private static volatile Singleton instance;

    //双重判断 + 同步锁
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }

}

分析:

既提高了效率也解决了线程安全问题,推荐使用这种方法

(7)懒汉式 - 静态内部类(推荐使用)

代码实现:

/**
 * 设计模式之单例模式
 * 静态内部类
 *
 * @author song
 * @create 2020/3/4 15:08
 */
public class SingletonTest07 {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println("两次获取的实例一样吗:" + (instance1 == instance2)); //true
    }
}

class Singleton {

    //私有构造方法,使其不可在外部通过构造器实例化
    private Singleton() {

    }

    //静态内部类
    private static class SingletonInstance{
        private final static Singleton INSTANCE = new Singleton();
    }

    //获取实例
    public static Singleton getInstance() {
        return SingletonInstance.INSTANCE;
    }

}

分析:

利用了类加载机制,保证初始化实例时只有一个线程。Singleton类被装载时并不会被实例化,当调用getInstance方法时才会装载SingletonInstance

(8)懒汉式 - 枚举法(推荐使用)

代码实现:

/**
 * 设计模式之单例模式
 * 枚举
 *
 * @author song
 * @create 2020/3/4 16:41
 */
public class SingletonTest08 {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.INSTANCE;
        Singleton instance2 = Singleton.INSTANCE;
        System.out.println("两次获取的实例一样吗:" + (instance1 == instance2)); //true
    }
}

enum Singleton{
    INSTANCE;
}

分析:

不仅能规避线程不安全,还能防止反序列化重新创建新的对象

二、工厂模式

(1)简单工厂模式

  • 简单工厂模式是属于创建型模式,是工厂模式的一种。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。
  • 定义了一个创建对象的类,由这个类来封装实例化对象的行为
  • 在软件开发中,当我们会用到大量的创建某种、某类或某批对象时,就会用到工厂模式

(2)工厂方法模式

定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类进行。

(3)抽象工厂模式

  • 定义了一个interface用于创建相关或有依赖关系的对象簇,而无需指明具体的类
  • 抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合
  • 抽象工厂模式就是对简单工厂模式的进一步抽象
  • 将工厂抽象成两层,AbsFactory(抽象工厂)和具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样将单个的简单工厂类变成了工厂簇,更利于代码的维护和扩展。

深入理解工厂模式

三、原型模式

原型模式是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。

原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节

可以通过重写clone方法实现拷贝,拷贝又分为浅拷贝和深拷贝

浅拷贝:

  • 对于基本数据类型的成员变量,浅拷贝会直接进行值传递,将该属性值复制一份给新的对象

  • 对于引用类型的成员变量,浅拷贝知识将该成员变量的引用值(内存地址)复制一份给新的对象,因此会造成一个对象修改值影响另外一个对象

  • 浅拷贝时使用默认的clone()方法来实现,例如:

    Person = (Person)super.clone()

深拷贝:

  • 复制对象的所有基本数据类型的成员变量值
  • 为所有引用类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象
  • 可通过重写clone方法和对象序列化方式(推荐)实现
//使用clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
    Company company = null;
    try {
        company = (Company) super.clone();
        //处理引用类型
        company.employee = (Employee) employee.clone();
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
    }
    return company;
}
//使用序列化
protected Object deepClone(){
    ByteArrayInputStream bis = null;
    ByteArrayOutputStream bos = null;
    ObjectInputStream ois = null;
    ObjectOutputStream oos = null;

    try {
        //序列化
        bos = new ByteArrayOutputStream();
        oos = new ObjectOutputStream(bos);
        oos.writeObject(this);

        //反序列化
        bis = new ByteArrayInputStream(bos.toByteArray());
        ois = new ObjectInputStream(bis);
        return ois.readObject();
    }catch (Exception e){
        e.printStackTrace();
    }
    return null;
}

原型模式的分析:

  • 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率
  • 不用重新初始化对象,而是动态的获取对象运行时的状态
  • 如果原始对象发生变化(增加或减少属性),其它克隆对象也会发生变化,无需修改代码
  • 浅拷贝和深拷贝对引用类型的处理方式不一样