Java设计模式-单例模式

设计模式之单例模式

单例模式是设计模式中,关于生产对象类型的设计模式的一种!

单例模式的具体表现是不可以获取同一个类的多个对象,反复获取也只会得到同一个对象!

假设需要将King类设计为单例的,首先,普通的类的代码例如:

public class King {
}

这样的类是可以随意创建对象的,例如:

King k1 = new King();
King k2 = new King();
King k3 = new King();

显然,随意创建对象是违背了单例模式的设计思想的,所以,为了避免随意对象,可以:

public class King {
  private King() {
    // 将构造方法私有,使之不可以在类的外部来创建对象
  }
}

当然,私有化构造方法的目的并不是“不允许创建对象”,而是“不允许随意创建对象”,为了保证在类的外部依然可以获取当前类的对象,还可以在类中添加获取对象的方法:

public class King {
  private King() {
    // 将构造方法私有,使之不可以在类的外部来创建对象
  }
  
  public King getInstance() {
    return new King();
  }
}

则外部通过调用getInstance()方法仍可以获取对象,但是,每调用一次,都会创建一个新的对象,所以,还需要进一步调整:

public class King {
  private King king = new King();
  
  private King() {
    // 将构造方法私有,使之不可以在类的外部来创建对象
  }
  
  public King getInstance() {
    return king; // 直接返回私有的全局属性
  }
}

由于在初始化时,private King king = new King();只会执行1次,反复调用getInstance()方法,获取到的都是同一个对象!这就符合了单例模式的设计思想!

但是,在以上代码中,如果没有King的对象,是无法调用getInstance()方法,而获取对象的唯一途径就是要调用这个方法!所以,为了解决这个矛盾,需要使用static修饰这个方法:

public class King {
  private King king = new King();
  
  private King() {
    // 将构造方法私有,使之不可以在类的外部来创建对象
  }
  
  public static King getInstance() {
    return king; // 直接返回私有的全局属性
  }
}

一旦在getInstance()方法的声明中添加了static关键字,还需要在private King king = new King();这个属性的声明中也补充static关键字,因为“被static修饰的成员不可以直接访问没被static修饰的成员”,所以,代码还需要改成:

public class King {
  private static King king = new King();
  
  private King() {
    // 将构造方法私有,使之不可以在类的外部来创建对象
  }
  
  public static King getInstance() {
    return king; // 直接返回私有的全局属性
  }
}

至此,单例模式就设计完成!

以上单例的代码是“饿汉式单例模式”,另外,还有“懒汉式单例模式”,其特征是“不到逼不得已,不创建对象”!例如:

public class King {
  private static King king;
  
  private King() {
    // 将构造方法私有,使之不可以在类的外部来创建对象
  }
  
  public static King getInstance() {
    // 判断全局的king是否被创建了对象,如果没有,则创建
    if (king == null) {
      king = new King();
    }
    return king;
  }
}

但是,以上代码是存在线程安全风险的!当存在多个“同时”运行的线程时,是可能创建出多个对象的!为了解决线程安全问题,需要:

public class King {
  private static King king;
  private static final Object lock = new Object();
  
  private King() {
    // 将构造方法私有,使之不可以在类的外部来创建对象
  }
  
  public static King getInstance() {
    // 为了解决线程安全问题,需要锁上以下代码
    synchronized(lock) {
      // 判断全局的king是否被创建了对象,如果没有,则创建
      if (king == null) {
        king = new King();
      }
    }
    return king;
  }
}

但是,一旦加了锁,效率又会变得比较低下!因为每次调用以上方法都需要锁上代码才可以继续向后执行,在多线程的应用场景中,多个“同时”运行的线程在执行这段代码是互斥的,为了解决效率偏低的问题,还会:

public class King {
  private static King king;
  private static final Object lock = new Object();
  
  private King() {
    // 将构造方法私有,使之不可以在类的外部来创建对象
  }
  
  public static King getInstance() {
    // 判断是否有必要锁住代码
    if (king == null) {
      // 为了解决线程安全问题,需要锁上以下代码
      synchronized(lock) {
        // 判断全局的king是否被创建了对象,如果没有,则创建
        if (king == null) {
          king = new King();
        }
      }
    }
    return king;
  }
}

至此,懒汉式的单例模式也就完成了!

总的来说,饿汉式的特点是:刚刚加载时就创建了对象,而懒汉式的特点是:当第1次尝试获取对象时才创建对象!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值