欢迎来到刘志红的博客

别人说程序员是“搬砖”的码农,但我们知道自己是追求个性的艺术家。也许我们不会过多的在意自己的外表和穿着,但在不羁的外表下,骨子里追求着代码的美、系统的美、设计的美。
扩大
缩小

effective解读-第三条 构建单例

单例:实例化一次的类,例如Spring容器通过IOC构建的Bean默认为单例模式

实现单例的方式

1. 静态域

优势:1. API简单清晰 2.简单(代码少,容易理解)

缺点:预防反射攻击和提供序列化需要更多代码的支持

public class SingletonInstance {
    public static final SingletonInstance singletonInstance=new SingletonInstance();
    private SingletonInstance(){
    }
    public void method(){
        System.out.println(this);
    }
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //创建实例
        SingletonInstance singletonInstance = SingletonInstance.singletonInstance;
        //反射创建实例
        SingletonInstance.singletonInstance.method();
        Constructor constructor = SingletonInstance.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        SingletonInstance o = (SingletonInstance) constructor.newInstance();
        //结果为false,即该方式可能被AccessibleObject.setAccessible方式攻击
        System.out.println(o==singletonInstance);
    }
}

解决办法

/**
 * @author Programmer_Liu.
 * @since 2021/3/25 16:32
 */
public class SingletonInstance {
    private static  boolean flag = true;
    public static final SingletonInstance singletonInstance = new SingletonInstance();
    private SingletonInstance() {
        if (flag){
            flag = false;
        }else{
            throw new RuntimeException("重复构建对象");
        }
    }
​
    public void method() {
        System.out.println(this);
    }
}
class Main{
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        SingletonInstance singletonInstance = SingletonInstance.singletonInstance;
        SingletonInstance.singletonInstance.method();
        Constructor constructor = SingletonInstance.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        SingletonInstance o = (SingletonInstance) constructor.newInstance();
        System.out.println(o == singletonInstance);
    }  
}

支持序列化

  1. 实现序列化接口

  2. 提供readResolve()方法

public class SingletonInstance implements Serializable {
    private static  boolean flag = true;
    public static final SingletonInstance singletonInstance = new SingletonInstance();
    private SingletonInstance() {
        if (flag){
            flag = false;
        }else{
            throw new RuntimeException("重复构建对象");
        }
    }
    public void method() {
        System.out.println(this);
    }
​
    /**
     *保证反序列化单例的唯一
     */
    public Object readResolve(){
        return singletonInstance;
    }
}
class Main{
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException, ClassNotFoundException {
        SingletonInstance singletonInstance = SingletonInstance.singletonInstance;
        SingletonInstance.singletonInstance.method();
        //Constructor constructor = SingletonInstance.class.getDeclaredConstructor();
        //constructor.setAccessible(true);
        //SingletonInstance o = (SingletonInstance) constructor.newInstance();
        //System.out.println(o == singletonInstance);
        //序列化
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("C:\\Users\\管理员\\Desktop\\objectFile.obj"));
        out.writeObject(singletonInstance);
​
        //反序列化
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("C:\\Users\\管理员\\Desktop\\objectFile.obj"));
        SingletonInstance singletonInstance1 = (SingletonInstance)in.readObject();
        //反序列化
        ObjectInputStream in2 = new ObjectInputStream(new FileInputStream("C:\\Users\\管理员\\Desktop\\objectFile.obj"));
        SingletonInstance singletonInstance2 = (SingletonInstance)in2.readObject();
        System.out.println(singletonInstance2==singletonInstance1);
    }
}

2.静态工厂方式

优势:相比静态域的方式更灵活 1.不改变API的前提下可以改为每个线程返回唯一实例2.可以编写泛型类型3.使用者可以使用方法引用

至少需要上面的优势之一,否则应该有限考虑静态域的方式

缺点:比较复杂而且预防反射攻击和提供序列化需要更多代码的支持

//静态工厂方式的单例模式
public class SingletonInstance {
    private static  boolean flag = true;
    private static final SingletonInstance singletonInstance = new SingletonInstance();
    private SingletonInstance() {
        if (flag){
            flag = false;
        }else{
            throw new RuntimeException("重复构建对象");
        }
    }
    public static SingletonInstance getInstance(){
        return singletonInstance;
    }
    public void method() {
        System.out.println(this);
    }
​
}
class Main{
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        SingletonInstance singletonInstance = SingletonInstance.getInstance();
        singletonInstance.method();
    }
}

1.线程单例

public class SingletonInstance {
    private static ThreadLocal<Boolean> flag = ThreadLocal.withInitial(() -> Boolean.TRUE);
    private static final ThreadLocal<SingletonInstance> LOCAL = ThreadLocal.withInitial(SingletonInstance::new);
    private SingletonInstance() {
        if (flag.get()) {
            flag.set(false);
        } else {
            throw new RuntimeException("重复构建对象");
        }
    }
​
    public static SingletonInstance getInstance() {
        SingletonInstance singletonInstance = LOCAL.get();
        return singletonInstance;
    }
​
    public void method() {
        System.out.println(this);
    }
​
}
​
class Main {
    public static void main(String[] args) throws InterruptedException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        SingletonInstance singletonInstance = SingletonInstance.getInstance();
        singletonInstance.method();
        //Constructor constructor = SingletonInstance.class.getDeclaredConstructor();
        //constructor.setAccessible(true);
        //SingletonInstance o = (SingletonInstance) constructor.newInstance();
        new Thread(() -> {
            SingletonInstance singletonInstance2 = SingletonInstance.getInstance();
            singletonInstance2.method();
        }).start();
        Thread.sleep(2000);
        SingletonInstance singletonInstance3 = SingletonInstance.getInstance();
        singletonInstance3.method();
    }
}

2.泛型单例工厂

//接口
public interface UnaryFunction<T> {
    T apply(T args);
}
//工厂
public class UnaryFunctionFactory {
    private static UnaryFunction<T> unaryFunction = args -> args;
​
    /**
     * 需要返回什么类型就转为什么类型
     * 使用层面:用什么类型接收就转为什么类型
     */
    public static<T> UnaryFunction<T> getInstance(){
        return (UnaryFunction<T>)unaryFunction;
    }
​
    public static void main(String[] args) {
        //用Integer类型接收
        UnaryFunction<Integer> s1=UnaryFunctionFactory.getInstance();
        Integer apply = s1.apply(111);
        //用String类型接收
        UnaryFunction<String> s2=UnaryFunctionFactory.getInstance();
        String hello = s2.apply("hello");
    }
}
​

3.方法引用

public class SingletonInstance{
    private static boolean flag = true;
    private static final SingletonInstance singletonInstance = new SingletonInstance();
    private SingletonInstance() {
        if (flag) {
            flag = false;
        } else {
            throw new RuntimeException("重复构建对象");
        }
    }
    public static SingletonInstance getInstance() {
        return singletonInstance;
    }
    public void method() {
        System.out.println(this);
    }
}
​
class Main {
    //提供者接口中可以直接通过方法引用调用
    public SingletonInstance s(Supplier<SingletonInstance> s){
        return s.get();
    }
    public static void main(String[] args){
        Main main = new Main();
        SingletonInstance s = main.s(SingletonInstance::getInstance);
        s.method();
    }
}

3. 单元素枚举

优势:更加简单,内部提供了序列化机制并预防反射攻击。单元素枚举是实现Singleton的最佳方案。

缺点:无法继承超类,因为枚举类型默认继承了Enum类

 

posted on 2021-03-26 12:43  MR_程序猿刘  阅读(79)  评论(0编辑  收藏  举报

导航