1

单例模式:

  1. 定义:一个类只有一个实例,该实例提供全局唯一访问点。
  2. 应用场景:全局配置文件,线程池,数据库连接池。
  3. 实现单例模式要考虑多个维度:

 1)懒加载
 2)线程安全
 3)能否防止反射入侵——通过反射的方式是可以访问私有的方法的,而我们防止对象被多次实例化的方式之一是将类的构造器设置 为私有的,所以反射可以破坏这个条件
防止反射入侵可以在构造方法中抛出异常来达到目的,在第二次调用构造器的时候直接抛出异常。如下:

private Singleton(){
    if(Singleton != null){
        throw new RuntimeException("单例构造器仅能被调用一次");
    }
}

如下代码验证通过反射可以生成两个不同的实例:

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Singleton {
    private static Singleton instance = null;

    private Singleton() { }

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

    public static void main(String[] args) throws NoSuchMethodException,
            IllegalAccessException, InvocationTargetException, 
            InstantiationException {
        Singleton singleton = Singleton.getInstance();

        Constructor<Singleton> constructor = Singleton.class
            .getDeclaredConstructor();
        constructor.setAccessible(true);
        Singleton newSingleton = constructor.newInstance();
        System.out.println(singleton == newSingleton);
    }
}

 4)反序列化时是否安全,防止反序列化时生成多个对象的方式是添加一个readResolve方法,在反序列化时会调用我们
自定义的readResolve方法,我们再在反序列化时调用我们添加的readResolve方法返回当前单例对象

private Object readResolve(){
    return singleton;
}

如下代码验证通过反序列化可以生成两个不同实例:

import java.io.*;
public class Singleton implements Serializable{

    private static Singleton instance = null;

    private Singleton() { }

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }

        return instance;
    }

    public static void main(String[] args) throws IOException, 
      ClassNotFoundException {
        Singleton instance = Singleton.getInstance();

        File singletonFile = new File("singleton");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(
            new FileOutputStream(singletonFile));
        objectOutputStream.writeObject(instance);

        ObjectInputStream objectInputStream = new ObjectInputStream(
            new FileInputStream(singletonFile));
        Singleton newInstance = (Singleton) objectInputStream.readObject();

        System.out.println(instance == newInstance);
    }
}

单例模式分为两种情况:
1、懒汉模式:
 1)定义:懒汉模式是指在需要使用这个类时才去加载这个类;
 2)使用场景:(1)实例化过程比较耗时;(2)某个功能模块在用户场景下并不会使用。
 3)实现方式:
a、

/**
 * 懒加载但线程不安全
 */
public class Singleton {

    private static Singleton instance = null;

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

b、

/**
 * 懒加载且线程安全但效率低
 */
public class Singleton {

    private static Singleton instance = null;

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

c、

/**
 * 懒加载且线程安全,通过缩小加锁范围,因而效率更高
 */
public class Singleton {

    private static Singleton instance = null;

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

d、

/**
 * 懒加载,线程安全
 *
 */
public class Singleton {

    private static Singleton instance = null;

    private Singleton() { }

    public static Singleton getInstance() {
    }

    // 两个知识点决定了该种方式实现了懒加载:
    // 1、类只有在访问到对应的成员才被加载——在这里,只有访问到SingleHolder
    //  .singleton时才会被加载;
    // 2、加载一个类时,其静态内部类不会被同时加载。
    private static class SingletonHolder {
        private static final Singleton singleton = new Singleton();
    }
}

2、饿汉模式:在第一次使用之前就加载好。
 1)定义:在第一次使用之前就初始化
 2)使用场景:程序启动后需要立即访问
 3)实现方式:
a、

/**
 * 线程安全
 */
public class Singleton{

    private static Singleton instance = new Singleton();

    public static Singleton getInstance() {
        return instance;
    }
}

b、

/**
 * 线程安全,防止反射入侵和序列化问题,同时这也是《java effective》中推荐的创建单例模式的方式
 */
public enum Singleton {
    // 1、通过枚举是如何做到线程安全的?
    //  枚举类型经过编译之后会生成public static final Single SINGLETON的字段,
    //  并且在静态块中对该字段进行初始化
    //  JVM在类加载过程中保证静态块在多线程环境下安全地执行
    // 2、通过枚举如下做到单例的?
    //  枚举类的构造器是私有的
    // 3、如何防止反射入侵的?
    //  1)通过反射获取构造方法无法获取枚举类的构造方法
    //  2) 通过反射实例化反射类型时会判断是否枚举类型,如果是枚举类型会抛出异常
    //  以上两点均由JVM保证
    // 4、如何防止反序列问题的?
    //  执行反序列化时会调用针对枚举类的方法:readEnum,该方法保证了反序列化时不会实例化
    //  多个对象。同样,这种方式由JVM提供
    SINGLETON {
        public void doSomething() {
            // doSomething
        }
    };
    public abstract void doSomething ();
}

水一水
39 声望5 粉丝

总结经验,提升自己