栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

懒汉单例模式(double check,静态内部类,枚举的写法)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

懒汉单例模式(double check,静态内部类,枚举的写法)

一、double check+synchronized

优点:懒加载(不用不占空间),正常方式获取的对象全局唯一,线程安全。

缺点:一个线程进入同步块,其余线程只能等待,浪费资源;可以使用反射和序列化破坏单例模式

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


public class LazyDoubleCheckSingleton implements Serializable{
    //泛型要等到实例化阶段才能被确定,静态变量在类加载过程就已经初始化,加了泛型加载时无法确定类型,所以不能给静态变量加泛型
    private volatile static LazyDoubleCheckSingleton instance;
    //数据存储
    private T data;

    private LazyDoubleCheckSingleton(){}   //私有化构造器,防止类外使用构造器实例化

    public static LazyDoubleCheckSingleton getInstance(Class clazz){   //全局单例获取点
        //检查是否要阻塞
        if (instance == null){
            synchronized (LazyDoubleCheckSingleton.class){
                //检查是否要重新创建实例
                if (instance == null){
                    instance = new LazyDoubleCheckSingleton();
                }
            }
        }
        return instance;
    }

    public T getData() {
        return data;
    }

    public LazyDoubleCheckSingleton setData(T data) {
        this.data = data;
        return this;
    }
}

class Test01{
    public static void main(String[] args) {
        for (int i = 0;i < 100;++i){
            int j = i+1;
            new Thread(()->{
                System.out.println(( "第"+j+"次获取对象的地址"+LazyDoubleCheckSingleton.getInstance(null)));
            }).start();
        }
    }
}

class Test02{
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Constructor declaredConstructor = LazyDoubleCheckSingleton.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        System.out.println("反射对象的地址:"+declaredConstructor.newInstance());
        System.out.println("正常获取的对象的地址:"+LazyDoubleCheckSingleton.getInstance(null));
    }
}

class Test03{
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        byte[] bytes = null;

        ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(LazyDoubleCheckSingleton.getInstance(null));

        bytes = bos.toByteArray();

        ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
        LazyDoubleCheckSingleton o = ((LazyDoubleCheckSingleton) new ObjectInputStream(bis).readObject());


        System.out.println("正常获取的对象的地址:"+LazyDoubleCheckSingleton.getInstance(null));
        System.out.println("序列化获取的对象的地址:"+o);
    }
}

运行Test01的结果截图如下,多线程每次获取的对象都是同一个

运行Test02的结果如下图所示,使用反射和正常访问的对象不是同一个,所以反射可以破坏单例模式:

 运行Test03的结果如下图所示,使用序列化和正常访问的对象不是同一个,所以反射可以破坏单例模式:

 

二、静态内部类

优点:懒加载(不用不占空间),正常方式获取的对象全局唯一;类加载器线程安全,不用手动处理线程同步;相比于double check多线程环境下线程不用等待。

缺点:可以使用反射(虽然可以用抛异常来处理)和序列化破坏单例模式

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


public class LazyStaticInnerClassSingleton implements Serializable{
    //内部类不使用就不加载

    //私有化构造器
    private LazyStaticInnerClassSingleton(){
        if (LazyHolder.INSTANCE != null) throw new RuntimeException("不允许反射获取对象");
    }

    //全局唯一访问点
    public static LazyStaticInnerClassSingleton getInstance(){
        return LazyHolder.INSTANCE;
    }

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

class test01{
    //多线程安全
    public static void main(String[] args) {
        for (int i = 0;i < 100;++i){
            int j = i+1;
            new Thread(()->{
                System.out.println(( "第"+j+"次获取对象的地址"+LazyStaticInnerClassSingleton.getInstance(null)));
            }).start();
        }
    }
}

class Test002{
    public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
        Constructor declaredConstructor = LazyStaticInnerClassSingleton.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        LazyStaticInnerClassSingleton lazyStaticInnerClassSingleton = declaredConstructor.newInstance();

        System.out.println("正常获取的对象的地址:"+LazyStaticInnerClassSingleton.getInstance());
        System.out.println("反射获取的对象的地址:"+lazyStaticInnerClassSingleton);
    }
}

class Test003{
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        byte[] bytes = null;

        ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(LazyStaticInnerClassSingleton.getInstance());

        bytes = bos.toByteArray();

        ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
        LazyStaticInnerClassSingleton o = ((LazyStaticInnerClassSingleton) new ObjectInputStream(bis).readObject());


        System.out.println("正常获取的对象的地址:"+LazyStaticInnerClassSingleton.getInstance());
        System.out.println("序列化获取的对象的地址:"+o);
    }
}

运行test01的结果截图如下,多线程每次获取的对象都是同一个

 运行Test002的结果如下图所示,可以使用抛异常处理反射,如果不在构造器手动抛异常,则反射还是能成功构建对象,且破坏单例模式。

 运行Test003的结果如下图所示,使用序列化和正常访问的对象不是同一个,所以序列化可以破坏单例模式:

 三、枚举单例写法(终极武器,effective java一书推荐)

优点:线程安全,不能使用反射和序列化破坏单例模式

缺点:无

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


public enum EnumSingleton implements Serializable{
    INSTANCE;
    private Object data;

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public static EnumSingleton getInstance(){
        return INSTANCE;
    }
}

class Test05{
    public static void main(String[] args) {
        for (int i = 0;i < 100;++i){
            int j = i+1;
            new Thread(()->{
                System.out.println(( "第"+j+"次获取对象的地址"+LazyDoubleCheckSingleton.getInstance(null)));
            }).start();
        }
    }
}

class Test06{
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Constructor declaredConstructor = EnumSingleton.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        EnumSingleton enumSingleton = declaredConstructor.newInstance();

        System.out.println("正常获取的对象的地址:"+EnumSingleton.getInstance());
        System.out.println("反射获取的对象的地址:"+enumSingleton);
    }
}

class Test07{
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        byte[] bytes = null;

        ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(EnumSingleton.getInstance());

        bytes = bos.toByteArray();

        ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
        EnumSingleton o = ((EnumSingleton) new ObjectInputStream(bis).readObject());


        System.out.println("正常获取的对象的地址:"+EnumSingleton.getInstance());
        System.out.println("序列化获取的对象的地址:"+o);
    }
}

1.线程安全:

2.不允许反射

3.序列化对象为同一个

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/785060.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号