『设计模式』我就要一个对象,你别给我这么多好不好!---单例模式
引入
作为一个现代社会文明青年,我觉得一夫一妻制非常合理。有些男人富裕了点,就想多照顾几个女人的行为,真的不可取,有的时候法律在这些面前显得难以生效,毕竟重婚罪又不能限制婚外情,多找几个对象。人尚且如此,何况程序呢,面对只能实例化一个对象的程序,我们该如何处理呢?我们今天就来看一下单例模式!
单例模式
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注意:
-
单例类只能有一个实例。 -
单例类必须自己创建自己的唯一实例。 -
单例类必须给所有其他对象提供这一实例。
目的:
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
主要解决:
一个全局使用的类频繁地创建与销毁。
何时使用:
当您想控制实例数目,节省系统资源的时候。
优点
实例控制:单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。灵活性:因为类控制了实例化过程,所以类可以灵活更改实例化过程。
缺点
开销:虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题。可能的开发混淆:使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用new关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。对象生存期:不能解决删除单个对象的问题。在提供内存管理的语言中(例如基于.NET Framework的语言),只有单例类能够导致实例被取消分配,因为它包含对该实例的私有引用。在某些语言中(如 C++),其他类可以删除对象实例,但这样会导致单例类中出现悬浮引用。不符合单一职责原则没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
实现:
单例模式的 UML 图
创建一个 Singleton 类
package 单例模式;
public class Singleton {
private static Singleton singleobj;
private Singleton() {}//通过private限定,不能通过new建立对象,也不能继承(可以有继承操作,但是无意义)
public Singleton newinstance()
{
if(singleobj!=null) return singleobj;
else {
singleobj=new Singleton();
return singleobj;
}
}
public void dosomething()
{
System.out.println("一个女朋友就够了!!!");
}
}
从 singleton 类获取唯一的对象
package 单例模式;
public class SingletonTest {
public static void main(String[] args) {
// SingleObject object = new SingleObject();
// 编译时错误:构造函数 SingleObject() 是不可见的
Singleton SingleT = Singleton.newinstance(); //获取唯一可用的对象
SingleT.dosomething();
}
}
执行程序,输出结果:
一个女朋友就够了!!!
进阶
看完前面的实列,但是并不能真正的应用在实际应用中,因为在现实的中更多的涉及到线程的问题,所以,给大家展示一下以下的方法。
1. 懒汉式(线程不安全,不支持多线程)
对比:
-
是否 Lazy 初始化:是 -
是否多线程安全:否 -
实现难度:易
描述: 这种方式是最基本的实现方式,也就是前面提到的方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作,在多线程系统的今天,一般很少使用。
实例
package 单例模式;
public class Singleton {
private static Singleton singleobj;
private Singleton() {}//通过private限定,不能通过new建立对象,也不能继承(可以有继承操作,但是无意义)
public Singleton newinstance(){
if(singleobj!=null) return singleobj;
else {
singleobj=new Singleton();
return singleobj;
}
}
public void dosomething() {
System.out.println("一个女朋友就够了!!!");
}
}
2. 懒汉式(线程安全,支持多线程)
对比:
-
是否 Lazy 初始化:是 -
是否多线程安全:是 -
实现难度:易
描述: 这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步,可以说是一种以时间换空间的方法。
-
优点:第一次调用才初始化,避免内存浪费。 -
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率,虽然getInstance() 的性能对应用程序不是很关键(该方法使用不太频繁)
实例
package 单例模式;
public class Singleton {
private static Singleton singleobj;
private Singleton() {
}// 通过private限定,不能通过new建立对象,也不能继承(可以有继承操作,但是无意义)
public static synchronized Singleton getinstance() {//synchronized 才能保证多线程单例
if (singleobj != null)
return singleobj;
else {
singleobj = new Singleton();
return singleobj;
}
}
public void dosomething() {
System.out.println("一个女朋友就够了!!!");
}
}
3. 饿汉式(线程安全,支持多线程)
对比:
-
是否 Lazy 初始化:否 -
是否多线程安全:是 -
实现难度:易
描述:这种方式比较常用,但容易产生垃圾对象。即以空间换时间的方式。优点:没有加锁,执行效率会提高。缺点:类加载时就初始化,浪费内存。它基于 classloader 机制避免了多线程的同步问题, 虽然避免了多线程时调用getinstance时的线程问题,但是不能避免的是其他静态方法也能创建singleton对象。
实例
package 单例模式;
public class Singleton {
private static Singleton singleobj = new Singleton();
private Singleton() {
}// 通过private限定,不能通过new建立对象,也不能继承(可以有继承操作,但是无意义)
public static synchronized Singleton getinstance() {
return singleobj;
}
public void dosomething() {
System.out.println("一个女朋友就够了!!!");
}
}
4.双检锁/双重校验锁 "DCL,即 double-checked locking"(线程安全,支持多线程)
对比:
-
是否 Lazy 初始化:是 -
是否多线程安全:是 -
实现难度:较复杂
描述: 这种方式采用双锁机制,安全且在多线程情况下能保持高性能。getInstance() 的性能对应用程序很关键。
实例
package 单例模式;
public class Singleton {
private volatile static Singleton singleobj;
private Singleton() {
}// 通过private限定,不能通过new建立对象,也不能继承(可以有继承操作,但是无意义)
public static synchronized Singleton getinstance() {
if (singleobj == null) {
synchronized (Singleton.class) {
if (singleobj == null) {
singleobj = new Singleton();
}
}
}
return singleobj;
}
public void dosomething() {
System.out.println("一个女朋友就够了!!!");
}
}
其余方法: 登记式/静态内部类,枚举
应用实例
配置文件,如果被系统的很多地方使用,那么不能总是去打开文件吧,那样很占用资源,所以我们可以使用单例模式,返回配置文件对象。
package 单例模式;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;
public class Singel_Loder { //饿汉Hunger方式
private static Singel_Loder singleobj = new Singel_Loder();
private Singel_Loder() {
}// 通过private限定,不能通过new建立对象,也不能继承(可以有继承操作,但是无意义)
private static Properties props = new Properties();
static {
try {
props.load(new FileInputStream("配置文件.properties"));
} catch (FileNotFoundException e) {
e.printStackTrace();
System.exit(-1);
} catch (IOException e) {
System.exit(-1);
}
}
public static Singel_Loder getinstance() {
return singleobj;
}
public Properties getpro() {
return props;
}
}
package 单例模式;
import java.util.Properties;
public class SingletonTest {
public static void main(String[] args) {
Singel_Loder sloder=Singel_Loder.getinstance();
Properties pro=sloder.getpro();
/**
借助配置文件做一些事情
*/
}
}
如果有什么想看的,可以私信我,如果在能力范围内,我会发布相应的博文!感谢大家的阅读!😘你的点赞、收藏、关注是对我最大的鼓励!