单例模式

单例模式,是设计模式中的一种。

单例模式,其特点是:被设计为单例的类型,在同一时间内,该类型的对象只会存在1个!

假设存在King类:

public class King {}

作为一个普通的类,是可以在类的外部随意创建对象的,例如:

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

因为在King类中,没有声明构造方法,则编译器会自动的添加默认构造方法,也就是公有的、无参数的构造方法,所以,在类的外部才可以随意创建对象!以上代码中就创建了3个King类型的对象!

如果要实现单例,首先,就不能允许随意创建对象,可以显式的添加构造方法,并将其私有化,避免外部随意访问,例如:

public class King {
    private King() {}
}

一旦显式的添加了构造方法,编译器就不会再自动添加构造方法了,则在类的外部,将不可以再执行King k1 = new King();这类的代码了!

使用了私有的构造方法,并不影响在类的内部使用该构造方法,为了保证在类的外部依然可以获取类的对象,则可以:

public class King {
    private King() {}
    
    public King getInstance() {
        return new King();
    }
}

同时,为了保证“多次获取时,获取到的都是同一个对象”,则不能反复在方法中创建对象,而是改为:

public class King {
    private King king = new King();
    
    private King() {}
    
    public King getInstance() {
        return king;
    }
}

改为这样以后,无论执行多少次getInstance()方法,该方法返回的都是同一个king变量,而该变量的值只是在类被加载时赋值了1次而已,就实现了单例的效果!

当然,以上代码是矛盾的!因为,如果要调用getInstance()方法,必须先有King类型的对象,而得到King类型对象的唯一途径就是调用getInstance()方法!也就是说:不调用getInstance()方法就无法得到对象,但是,调用方法之前又必须有对象!

为了解决这个问题,可以为getInstance()方法添加static修饰符,则通过类名.方法名()的格式就可以调用方法了,不需要事先获取对象!同时,基于“被static修饰的成员不可以直接访问没被static修饰的成员”的原则,所以,全局的private King king = new King();也需要添加static修饰符:

public class King {
    private static King king = new King();
    
    private King() {}
    
    public static King getInstance() {
        return king;
    }
}

至此,简单的单例模式代码就完成了,后续,需要获取King类型的对象时,只能通过King.getInstance()得到对象,而该方法每次返回的都是同一个对象!
严格来说,以上单例模式得设计是“饿汉式”的,可以看到,在类被加载时,就直接创建了King类的对象,此时,也许并不需要获取对象,,后续当需要对象时,直接获取即可。还有一种是“懒汉式”的单例模式,其特点就是“不到逼不得已,不创建对象”!基础代码例如:

public class King {
    private static King king;
    
    private King() {}
    
    public static King getInstance() {
        if(king == null){
			king = new King();
		}
        return king;
    }
}

以上代码是多线程不安全的!为了解决这个问题,可以将以上代码快添加互斥锁,例如:

public class King {
    private static King king;
    
    private King() {}
    
    public static King getInstance() {
       synchronized (“java”) {
        if(king == null){
			king = new King();
		}
		}
        return king;
    }
}

一旦添加了锁,每次调用getInstance()方法时都会先锁定代码再执行,效率偏低,为此,还可以这样:

public class King {
    private static King king;
    
    private King() {}
    
    public static King getInstance() {
       if(king==null){
       	synchronized (“java”) {
       		if(king == null){
				king = new King();
			}
		}
		}
        return king;
    }
}

以上,就是完整的“懒汉式”单例模式!
注意:被static修饰的成员都是常驻内存的!