单例模式,顾名思义就是单个实例,就是说一个类有且仅有一个实例,应该是23中设计模式中最简单也最好理解的设计模式了,同时简单的往往也是重要的和常用的,对吧?简单的往往也是常用的,这句话在任何地方都能适用。
一、常见的几种写法
1、饿汉式(可以使用)
特点:线程安全的,不能实现延迟加载
public class HungryMan {
/**1、私有化构造器:所有的单例模式第一步永远都是私有化构造器,因为只有不让在外面调用构造器new
* 对象了,才能控制对象的数量,这是单例模式的前提
*/
private HungryMan(){}
//2、内的内部创建出实例
private static final HungryMan HUNGRY_MAN = new HungryMan();
//3、提供公共静态的方法返回实例
public static HungryMan getInstance(){
return HUNGRY_MAN;
}
}
1.1饿汉式之静态代码块版(可以使用)
特点:线程安全的,不能实现延迟加载
public class HungryMan1 {
private HungryMan1(){}
private static HungryMan1 hungryMan1;
static {
hungryMan1 = new HungryMan1();
}
public static HungryMan1 getInstance(){
return hungryMan1;
}
}
2、懒汉式(不推荐使用)
特点:不是线程安全的,能延迟加载
public class LazyMan {
private LazyMan(){}
private static LazyMan lazyMan;
public static LazyMan getInstance() {
if (lazyMan == null) {
lazyMan = new LazyMan();
}
return lazyMan;
}
}
2.1懒汉式之同步方法版(可以使用)
特点:线程安全的,能延迟加载,但效率低(当有多个线程同时调用时,不管有没有实例都要等待拿锁)
public class LazyMan1 {
private LazyMan1(){}
private static LazyMan1 lazyMan1;
public static synchronized LazyMan1 getInstance(){
if (lazyMan1 == null) {
lazyMan1 = new LazyMan1();
}
return lazyMan1;
}
}
2.2懒汉式之同步代码块版(可以使用)
特点:线程安全的,能延迟加载,但效率低
public class LazyMan2 {
private LazyMan2(){}
private static LazyMan2 lazyMan2;
public static LazyMan2 getInstance(){
synchronized (LazyMan2.class){
if (lazyMan2 == null) {
lazyMan2 = new LazyMan2();
}
return lazyMan2;
}
}
}
2.3懒汉式之双重检查版(较推荐使用)
特点:线程安全的,能延迟加载,效率高(据说双重检查有线程安全问题,不确定还)
public class LazyMan3 {
private LazyMan3(){}
private static LazyMan3 lazyMan3;
public static LazyMan3 getInstance(){
if (lazyMan3 == null){
synchronized (LazyMan3.class){
if (lazyMan3 == null) {
lazyMan3 = new LazyMan3();
}
}
}
return lazyMan3;
}
}
3、静态内部类版(推荐使用)
特点:线程安全,能延迟加载,效率高
public class StaticInsideClass {
private StaticInsideClass(){}
//在静态内部类中创建外部类的实例
//PS:静态内部类会等到它被调用的时候才加载
private static class InnerClass{
private static final StaticInsideClass STATIC_INSIDE_CLASS = new StaticInsideClass();
}
public static StaticInsideClass getInstance(){
return InnerClass.STATIC_INSIDE_CLASS;
}
}
4、枚举类版(推荐使用)
特点:线程安全的,效率高,能避免反射和反序列化创建对象的问题,不能延迟加载
public enum EnumSingleton {
//被枚举出来的对象
Instance;
/*当前枚举类的其他属性和方法*/
//名字
private String name;
//年龄
private Integer age;
public void doSomeThing(){
System.out.println("当前枚举出来的对象做一些事情~~~");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
5、登记式:用一个线程安全的Map来保存对象实例。在此就不写了,大家可以自己实现一下。
二、常见的应用场景
首先,想一下单例模式的特点:在一个系统中只有一个对象实例,我们可以好好设计这个类,比如设计一些读取和写入的方法,来对类中的属性(比如集合等)进行操作,因为就只有一个对象,该对象拥有自己的一套属性和方法,即有自己的内存空间,这样该类就能成为系统运行期间的一个小的内存数据库。(当前要保存该类不会被JVM当成垃圾回收了才行)
1、系统配置文件的读取时,对应一个单例的类。
2、网站计数器来记录网站访问的次数、在线人数等
3、临时对象的频繁创建和销毁时,比如需要创建一个对象来保存数据,然后进行插入数据库操作时。
4、各种池的创建都是单例模式的,比如数据库连接池、线程池等。
总之一句话,凡是需要保证一份资源只能有一份时,就用单例模式。
三、问题
这里我有个问题先记下来:为什么单例模式的对象不会被JVM当成垃圾回收?
答: 等我了解完JVM的知识后再来填坑吧
聪明的小伙伴看到后,能否在我填坑前,在评论区留言解答呢?说出你的看法,show time~~