什么是单例模式?
单例模式是一种软件设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问该实例。在单例模式中,类自身负责创建自己的唯一实例,并且保证在整个应用程序中只能访问到这个实例。
实现步骤:
- 私有化构造方法;
- 私有化构造方法,防止通过new 创建对象。
- 提供静态方法入口;
- 对外提供一个公开的静态方法,获取类的唯一实例入口。
- 定义成静态是为了能通过类访问,因为私有化构造方法,外界无法获取实例对象,所以不能通过实例对象访问。
- 定义静态变量;
- 定义一个私有的单例类的静态变量,在类加载的时候,初始化静态变量。
- 为什么是私有的:
- 保护单例类的安全性,禁止外界对单例类的改变。
- 保证单例类的唯一性,不能修改唯一实例对象。
- 为什么是静态的:
- 因为入口方法是静态的,静态方法中只能访问静态成员变量。
- 也符合静态的特征,在类加载时,初始化静态变量,且只执行一次。
实现(饿汉式):
1. 提前创建好单例实例对象。
2. 调用时,直接返回创建好的单例实例对象。
/**
* 单例模式:
* 饿汉式
*/
public class Singleton {private String name;// 唯一实例对象, 静态变量private static Singleton singleton = new Singleton();// 私有化构造方法; 外界就不能创建当前类对象了。private Singleton(){}// 提供一个外界可以获取唯一实例的入口public static Singleton getInstance(){return singleton;}public String getName(){return name;}public void setName(String name){this.name = name;}
}
实现(懒汉式):
1. 在用到时,才创建单例实例对象。
2. 调用时,判断是否已经创建过单例实例对象,如果创建过直接返回,否则创建再返回。
/**
* 单例模式:
* 懒汉式
*/
public class Singleton {private String name;// 唯一实例对象,但是不初始化private static Singleton singleton;// 访问入口,获取单例实例对象的唯一入口。public static Singleton getInstance(){// 如果对象为空,说明时第一次访问,创建单例实例对象。if( singleton == null){singleton = new Singleton();}return singleton;}public String getName(){reutnr name;}public void setName(String name){this.name = name;}
}
多线程环境实现:
public class Singleton {private static volatile Singleton instance;private Singleton() {// 私有构造函数}public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}protected Object readResolve() {return getInstance();}
}
在上述代码中,通过添加volatile
关键字修饰instance
变量,确保多线程环境下对该变量的可见性和有序性,避免了线程安全问题。
在静态方法getInstance()
中,首先检查instance
是否为null,如果为null才会进入同步块。在同步块内部再次判断instance
是否为null,这是为了防止多个线程同时通过第一个判断,并同时进入同步块创建实例,从而导致多个实例的产生。
此外,还需要注意到readResolve()
方法的存在。当单例类被序列化后再进行反序列化时,可能会生成新的实例,为了避免这种情况,可以添加readResolve()
方法,并在方法内返回单例实例。这样可以确保反序列化后获得的对象仍然是同一个实例。
这是一个完整版的单例模式示例代码,通过双重检查锁定和防止反射攻击的处理,保证了单例类的线程安全性和唯一性。
饿汉式和懒汉式的区别:
饿汉式和懒汉式是两种常见的单例模式实现方式,它们的区别主要体现在初始化时机和线程安全性上。
-
饿汉式:
- 在类加载的时候就创建了对象实例,无论是否使用都会创建。
- 在类加载过程中就完成了实例化,因此不存在多线程并发的问题。
- 可能会浪费一些资源,因为不管有没有使用到该实例,都会提前创建。
-
懒汉式:
- 延迟实例化,只有在第一次使用时才会创建对象实例。
- 在多线程环境下,需要考虑并发访问的线程安全问题。
- 相比饿汉式,懒汉式在不使用时不会提前创建实例,节省了一些资源。
需要注意的是,在多线程环境下使用懒汉式时,需要进行额外的处理来保证线程安全性,以避免多个线程同时创建多个实例。常见的线程安全处理方式包括使用 synchronized 关键字、双重检查锁定等。