创建者模式-单例模式(java)

1. 单例设计模式

  • 单例模式(Singleton Pattern)属于创建型模式,它提供了一种创建对象的最佳方式。

  • 单例模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。

2. 单例模式的实现

  • 饿汉式:类加载的时候创建对象

  • 懒汉式:首次使用的时候创建对象

3. 饿汉式

3.1 静态变量方式

  • 该类对象在类加载的时候创建,如果该对象足够大并且一直没有使用,就会造成内存的浪费
/**
 * 创建者模式 ----单例模式--饿汉式(静态成员变量)
 */
public class Singleton {

    //1.私有构造方法
    private Singleton() {
    }

    //2.创建对象
    private static Singleton instance = new Singleton();

    //3.获取对象方法
    public static Singleton getInstance() {
        return instance;
    }
}

3.2 静态代码块

  • 该类对象在类加载的时候创建,也会造成内存浪费问题
/**
 * 创建者模式 ----单例模式--饿汉式(静态代码块)
 */

public class Singleton {

    //1.私有构造方法
    private Singleton() {
    }

    //2.声明变量
    private static Singleton instance;

    //在静态代码块中创建该类对象
    static {
        instance = new Singleton();
    }

    //3.获取对象方法
    public static Singleton getInstance() {
        return instance;
    }

}

3.3 枚举方式

  • 枚举类型是线程安全的,并且只会装载一次,而且枚举类型是所用单例实现中唯一一种不会被破坏的单例实现模式。
/**
 * 创建者模式 ----单例模式--饿汉式(枚举方式)
 */
public enum Singleton {
    INSTANCE;
}

4. 懒汉式

4.1 静态方法获取对象(线程不安全)

  • 在对象被使用的时候创建对象,如果是多线程环境,会出现线程安全问题。
/**
 * 创建者模式 ----单例模式--懒汉式
 */

public class Singleton {

    //1.私有构造方法
    private Singleton() {
    }

    //2.声明变量
    private static Singleton instance;

    //3.1获取对象方法(线程不安全)
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

}

4.2 静态方法获取对象(线程安全)

  • 解决了线程安全问题。但是在方法上添加了synchronized关键字,导致方法的执行效果低(在初始化instance的时候才会出现线程安全问题)。
/**
 * 创建者模式 ----单例模式--懒汉式
 */

public class Singleton {

    //1.私有构造方法
    private Singleton() {
    }

    //2.声明变量
    private static Singleton instance;

    //3.2获取对象方法(线程安全--加锁)
    public static synchronized Singleton getConcurrentInstance1() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

}

4.3 静态方法获取该对象(双重检查锁)

  • 双重检查锁模式解决了单例、性能、线程安全问题,双重检测锁模式在多线程的情况下,可能会出现空指针问题,出现问题的原因是JVM在实例化对象的时候会进行优化和指令重排序操作。解决:使用 volatile 关键字, volatile 关键字可以保证可见性和有序性。
/**
 * 创建者模式 ----单例模式--懒汉式
 */

public class Singleton {

    //1.私有构造方法
    private Singleton() {
    }

    //2.声明变量
    private static volatile Singleton instance2;

    //获取对象方法(线程安全--双重检查锁)
    public static Singleton getConcurrentInstance3() {
        if (instance2 == null) {
            synchronized (Singleton.class) {
                if (instance2 == null) {
                    instance2 = new Singleton();
                }
            }
        }
        return instance2;
    }

}

4.4 静态内部类

  • 静态内部类单例模式中实例由内部类创建, JVM 在加载外部类的过程中,是不会加载静态内部类的,只有内部类的属性/方法被调用时才会被加载,并初始化其静态属性。静态属性由于被 static 修饰,保证只被实例化一次,并且严格保证实例化顺序。第一次加载Singleton类时不会去初始化INSTANCE,只有第一次调用getInstance,虚拟机加载SingletonHolder并初始化INSTANCE,这样不仅能确保线程安全,也能保证 Singleton 类的唯一性。
/**
 * 创建者模式 ----单例模式--懒汉式
 */

public class Singleton {

    //1.私有构造方法
    private Singleton() {
    }

    //3.5 静态内部类
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance4() {
        return SingletonHolder.INSTANCE;
    }

}

5. 破坏单例模式

  • 使上面定义的单例类(Singleton)可以创建多个对象,枚举方式除外。有两种方式,分别是序列化和反射。

5.1 序列化

  • Singleton类
import java.io.Serializable;
/**
 * 创建者模式 ----单例模式--饿汉式(静态成员变量)
 */
public class Singleton implements Serializable {

    //1.私有构造方法
    private Singleton() {
    }

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

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }

}
  • 测试类
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * 创建者模式 ----单例模式 --破坏单例模式
 */

public class Client {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, InterruptedException {

        // 1. 序列化方式
        writeObjectToFile();
        readObjectFromFile();
        readObjectFromFile();
    }

    //读取对象文件
    private static void readObjectFromFile() throws IOException, ClassNotFoundException {
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("E:\\obj.txt"));
        Singleton singleton = (Singleton) objectInputStream.readObject();
        objectInputStream.close();
        System.out.println(singleton);
    }

    //将对象写入文件
    private static void writeObjectToFile() throws IOException {
        Singleton instance = Singleton.getInstance();
        ObjectOutputStream objectOutStream = new ObjectOutputStream(new FileOutputStream("E:\\obj.txt"));
        objectOutStream.writeObject(instance);
        objectOutStream.close();
    }

}


解决:在Singleton类中添加readResolve()方法,在反序列化时被反射调用,如果定义了这个方法,就返回这个方法的值,如果没有定义,则返回新new出来的对象。

import java.io.Serializable;

/**
 * 创建者模式 ----单例模式--饿汉式(静态成员变量)
 */
public class Singleton implements Serializable {

    private static boolean flag = false;

    //1.私有构造方法
    private Singleton() {
        synchronized (Singleton.class) {
            System.out.println("单例对象创建......");
            if (flag) {
                throw new RuntimeException("单例对象已经创建,不能再次创建");
            }
            flag = true;
        }
    }

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

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }

    //解决序列化反序列化破解单例模式
    private Object readResolve() {
        return getInstance();
    }

}


5. 2 反射

  • 测试类
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * 创建者模式 ----单例模式 --破坏单例模式
 */

public class Client {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, InterruptedException {
        // 2. 反射方式
        // 2.1 获取对象字节码
        Class<Singleton> singletonClass = Singleton.class;
        // 2.2 获取无参构造方法
        Constructor<Singleton> constructor = singletonClass.getDeclaredConstructor();
        // 2.3 设置为可访问私有方法
        constructor.setAccessible(true);
        // 2.4 创建对象
        Singleton singleton1 = constructor.newInstance();
        Singleton singleton2 = constructor.newInstance();

        System.out.println(singleton1==singleton2);
    }

}


解决:当通过反射方式调用构造方法进行创建创建时,直接抛异常。不运行此中操作。

import java.io.Serializable;

/**
 * 创建者模式 ----单例模式--饿汉式(静态成员变量)
 */
public class Singleton implements Serializable {

    private static boolean flag = false;

    //1.私有构造方法
    private Singleton() {
        synchronized (Singleton.class) {
            System.out.println("单例对象创建......");
            if (flag) {
                throw new RuntimeException("单例对象已经创建,不能再次创建");
            }
            flag = true;
        }
    }

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

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }

}


posted @ 2024-11-25 21:28  94264yj  阅读(18)  评论(0)    收藏  举报