顾名思义,单例模式也就是说只有一个实例,这种模式也是属于很常用的一种设计模式,也很容易理解。当然,面试也是会常问到的。
保证一个类只有一个实例。 为什么会有人想要控制一个类所拥有的实例数量? 最常见的原因是控制某些共享资源 (例如数据库或文件) 的访问权限。
它的运作方式是这样的: 如果你创建了一个对象, 同时过一会儿后你决定再创建一个新对象, 此时你会获得之前已创建的对象, 而不是一个新对象。
为该实例提供一个全局访问节点。 还记得你 (好吧, 其实是我自己) 用过的那些存储重要对象的全局变量吗? 它们在使用上十分方便, 但同时也非常不安全, 因为任何代码都有可能覆盖掉那些变量的内容, 从而引发程序崩溃。
和全局变量一样, 单例模式也允许在程序的任何地方访问特定对象。 但是它可以保护该实例不被其他代码覆盖。
还有一点: 你不会希望解决同一个问题的代码分散在程序各处的。 因此更好的方式是将其放在同一个类中, 特别是当其他代码已经依赖这个类时更应该如此。
所以,要注意一点:单例模式并不是单单指一个类只能有一个实例,还有另一层意思:可以严格的控制全局变量。
如果程序中的某个类对于所有客户端只有一个可用的实例, 可以使用单例模式。
单例模式禁止通过除特殊构建方法以外的任何方式来创建自身类的对象。 该方法可以创建一个新对象, 但如果该对象已经被创建, 则返回已有的对象。
如果你需要更加严格地控制全局变量, 可以使用单例模式
单例模式与全局变量不同, 它保证类只存在一个实例。 除了单例类自己以外, 无法通过任何方式替换缓存的实例。请注意, 你可以随时调整限制并设定生成单例实例的数量, 只需修改 获取实例方法, 即 getInstance 中的代码即可实现。
在讲实现之前,先了解两个概念,单例模式的实现就分为这两种方式,分别是:饿汉式和懒汉式。
饿汉式(静态常量)
class Type1{
/**
* 构造器私有,外部不能new
*/
private Type1(){
}
/**
* 实例对象
*/
private static final Type1 instance = new Type1();
/**
* 获取实例的静态方法
*/
public static Type1 getInstance(){
return instance;
}
}
饿汉式(静态代码块)
class Type2{
/**
* 构造器私有,外部不能new
*/
private Type2(){
}
/**
* 实例对象
*/
private static Type2 instance;
/**
* 静态代码块
*/
static {
instance = new Type2();
}
/**
* 获取实例的静态方法
*/
public static Type2 getInstance(){
return instance;
}
}
优缺点同第一种,只是换了一种写法。
懒汉式(线程不安全)
class Type3{
/**
* 构造器私有,外部不能new
*/
private Type3(){
}
/**
* 实例对象
*/
private static Type3 instance;
/**
* 获取实例的静态方法,只有调用了才会创建实例
*/
public static Type3 getInstance(){
if(instance == null) instance = new Type3();
return instance;
}
}
懒汉式(线程安全,同步方法)
class Type4{
/**
* 构造器私有,外部不能new
*/
private Type4(){
}
/**
* 实例对象
*/
private volatile static Type4 instance;
/**
* 获取实例的静态方法,只有调用了才会创建实例,线程安全
*/
public static synchronized Type4 getInstance(){
if(instance == null) instance = new Type4();
return instance;
}
}
懒汉式(线程安全,同步代码块)
class Type5{
/**
* 构造器私有,外部不能new
*/
private Type5(){
}
/**
* 实例对象
*/
private volatile static Type5 instance;
/**
* 获取实例的静态方法,只有调用了才会创建实例
*/
public static Type5 getInstance(){
synchronized (Type5.class){
if(instance == null) instance = new Type5();
}
return instance;
}
}
懒汉式(双重检查)
class Type6{
/**
* 构造器私有,外部不能new
*/
private Type6(){
}
/**
* 实例对象
*/
private volatile static Type6 instance;
/**
* 获取实例的静态方法,只有调用了才会创建实例
*/
public static Type6 getInstance(){
if (instance == null){
synchronized (Type6.class){
if(instance == null) instance = new Type6();
}
}
return instance;
}
}
懒汉式(静态内部类)
class Type7{
/**
* 构造器私有,外部不能new
*/
private Type7(){
}
/**
* 写一个静态内部类,该类中有一个静态属性 INSTANCE
*/
private static class SingletonInstance {
private static final Type7 INSTANCE = new Type7();
}
/**
* 获取实例的静态方法,只有调用了才会创建实例
*/
public static Type7 getInstance(){
return SingletonInstance.INSTANCE;
}
}
优点:线程安全,懒加载
说明:静态内部类方式在 Singleton 类被装载时并不会立即实例化,而是在需要实例化时,调用 getInstance 方法,才会装载 SingletonInstance 类,从而完成 Singleton 的实例化。
类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM 帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
结论:建议使用!
懒汉式(枚举)
public class SingletonTest08 {
public static void main(String[] args) {
Singleton instance = Singleton.INSTANCE;
Singleton instance2 = Singleton.INSTANCE;
System.out.println(instance == instance2);
System.out.println(instance.hashCode());
System.out.println(instance2.hashCode());
instance.sayOK();
}
//使用枚举,可以实现单例, 推荐
enum Singleton {
//属性
INSTANCE;
public void sayOK() {
System.out.println("ok~");
}
}
}