欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 文化 > 设计模式-单例模式

设计模式-单例模式

2025/1/19 5:36:51 来源:https://blog.csdn.net/weixin_41645817/article/details/145213819  浏览:    关键词:设计模式-单例模式

定义

        保证一个类仅有一个实例,并提供一个访问它的全局访问点。

类图

类型

饿汉式

        线程安全,调用效率高,但是不能延迟加载。

public class HungrySingleton
{private static final HungrySingleton instance=new HungrySingleton();private HungrySingleton(){}public static HungrySingleton getInstance(){return instance;}
}

懒汉式

        线程安全,调用效率不高,并且可以延迟加载。

public class LazySingleton
{private static volatile LazySingleton instance=null;    //保证 instance 在所有线程中同步private LazySingleton(){}    //private 避免类在外部被实例化public static synchronized LazySingleton getInstance(){//getInstance 方法前加同步if(instance==null){instance=new LazySingleton();}return instance;}
}

双重检查锁式

        双重检查锁模式解决了单例、性能、线程安全问题。但在多线程情况下,可能会出现空指针问题。问题在于JVM 在实例化对象时会进行优化和指令重排序操作。解决空指针问题只需使用volatile 关键字,volatile 可以保证可见性和有序性。

public class LazyMan3 {private LazyMan3(){}private static volatile LazyMan3 instance;public static LazyMan3 getInstance(){//第一次判断,如果instance不为null,不需要抢占锁,直接返回对象if (instance == null){synchronized (LazyMan3.class){//第二次判断if (instance == null){instance = new LazyMan3();}}}return instance;}
}
class LazyMan3Test{public static void main(String[] args) {LazyMan3 instance = LazyMan3.getInstance();LazyMan3 instance1 = LazyMan3.getInstance();System.out.println(instance == instance1);}
}

静态内部类式

        线程安全,调用效率不高,可以延迟加载;


public class LazyMan4 {private LazyMan4(){}//定义一个静态内部类private static class LazyMan4Holder{private static final LazyMan4 INSYANCE = new LazyMan4();}//对外访问方法public static LazyMan4 getInstance(){return LazyMan4Holder.INSYANCE;}
}
class LazyMan4Test{public static void main(String[] args) {LazyMan4 instance = LazyMan4.getInstance();LazyMan4 instance1 = LazyMan4.getInstance();System.out.println(instance == instance1);}
}

静态代码块式

public class HungryChinese2 {//私有构造方法,为了不让外界创建该类的对象private HungryChinese2(){}//声明该类类型的变量private static HungryChinese2 hungryChinese2;//初始值为null//静态代码块中赋值static {hungryChinese2 = new HungryChinese2();}//对外提供的访问方式public static HungryChinese2 getInstance(){return hungryChinese2;}
}
class HungryChinese2Test{public static void main(String[] args) {HungryChinese2 instance = HungryChinese2.getInstance();HungryChinese2 instance1 = HungryChinese2.getInstance();System.out.println(instance.equals(instance1));}
}

枚举式

        线程安全,调用效率高,不能延迟加载,但是可以天然的防止反射和反序列化漏洞。

public enum LazyMan5 {INSTANCE;
}
class LazyMan5Test{public static void main(String[] args) {LazyMan5 instance = LazyMan5.INSTANCE;LazyMan5 instance1 = LazyMan5.INSTANCE;System.out.println(instance == instance1);}
}

应用场景

  1. 某个类只要求生成一个对象的时候。

  2. 当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如Web中的配置对象、数据库的连接池等。

  3. 当某类需要频繁实例化,而创建的对象又频繁被销毁时,如多线程的线程池、网络连接池等。

单例问题

Serializable

  • 问题:如果单例类实现了java.io.Serializable 接口,那么这个类可能会被反序列化,并且反序列化多次使用同一对象时,会得到多个单例类的实例,这样就不是单例了;
  • 解决方法:需要添加如下方法
// 反序列化时,如果定义了readResolve() 则直接返回此方法指定的对象,而不需要单独再创建新对象了;
private Object readResovle() throws ObjectStreamException{// TODO Auto-generated method stubreturn instance;
}

在Android 中使用单例模式可能会内存泄漏

  • 问题:当调用getInstance 时,如果传入的context是Activity 的context 。只要这个单例没有被释放,那么这个Activity 也不会被释放,一直到进程退出才会释放。

public class CommUtils {private volatile static CommUtils mCommUtils;private Context mContext;public CommUtils(Context context) {mContext=context;}public static  CommUtils getInstance(Context context) {if (mCommUtils == null) {synchronized (CommUtils.class) {if (mCommUtils == null) {mCommUtils = new CommUtils(context);}}}return mCommUtils;}
}
  • 解决方法:能使用Application的Context就不要使用Activity的Context,Application的生命周期伴随着整个进程的周期。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com