单例模式(Java语言)
`
文章目录
一、单例模式的定义
单例(Singleton)模式的定义:程序运行时,在java虚拟机中只存在该类的一个实例对象。
二、单例模式的特点
1、单例类只有一个实例对象
2、该单例对象必须由单例类自行创建
3、单例类对外提供一个访问该单例的全局访问点
三、单例模式的结构
单例模式是设计模式中最简单的模式之一。通常,普通类的构造函数是公有的,外部类可以通过“new 构造函数()”来生成多个实例。但是,如果将类的构造函数设为私有的,外部类就无法调用该构造函数,也就无法生成多个实例。这时该类自身必须定义一个静态私有实例,并向外提供一个静态的公有函数用于创建或获取该静态私有实例。
结构
四、单例模式的实现
饿汉模式
public class singlePattern {
//先创建一个表示单例的类
//我们就要求Singleton这个类只能有一个实例
//饿汉模式的单例实现
//饿汉模式的单例实现,“饿”指得是,只要类被加载,实例就会立刻创建(实例创建时机比较早)
static class Singleton{
//把 构造方法 变为私有,此时在该类外部,就无法 new 这个类的实例了
private Singleton(){
}
//再来创建一个 static 的成员,表示Singleton 类唯一的实例
//static 和 类相关,和实例无关,类在内存中只有一份,static 成员也就只有一份
static Singleton instance = new Singleton();
//new没报错是因为Singleton类是singlePattern的内部类,singlePattern是可以访问内部类的private成员的
public static Singleton getInstance(){
return instance;
}
public static void main(String[] args) {
//此处得 getInstance 就是获取实例得唯一方式,不应该使用其他方式创建实例了
Singleton s = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s == s2);
}
}
}
饿汉式单例在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变(final ),所以是线程安全的,可以直接用于多线程而不会出现问题。但对象提前创建,所以会占据一定的内存,内存占用大以空间换时间(创建的单例对象可能没有被使用到)
懒汉模式
public class lazyPattern {
//使用懒汉模式来实现,Singleton类被加载的时候,不会立刻实例化
//等到第一次使用这个实例的时候,再实例化
static class Singleton{
private static Singleton instance = null;
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
public static void main(String[] args) {
}
}
类被加载的时候,没有立刻被实例化,第一次调用getInstance的时候,才真正的实例化。
如果要是代码一整场都没有调用getInstance,此时实例化的过程也就被省略掉了,又称“延时加载”
一般认为“懒汉模式” 比 “饿汉模式”效率更高。
懒汉模式有很大的可能是“实例用不到”,此时就节省了实例化的开销。
双重检查懒汉模式
static class Singleton {
//为了解决内存不可见问题,需要加上关键字volatile
private volatile static Singleton instance = null;
public static Singleton getInstance1() {
if(instance == null){
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
普通懒汉模式是线程不安全的,双重检查懒汉模式:加synchronized 锁保证线程安全,双重if保证效率,volatile保证内存可见性
静态内部类(懒汉模式)
public class Singleton {
//私有构造器
private Singleton (){}
//静态内部类的方式,只实例化一次,后面每次调用外部类都不会加载内部类
private static class SingletoHolder{
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
public static void main(String[] args) {
}
}
外部类加载的时候不会加载内部类,所以使用静态内部类的方式创建单例可以保证线程安全,同时也可以实现懒加载
枚举Enum
static enum EnumSingleton {
/**
* 实例对象
*/
INSTANCE;
private final TestClass instance;
EnumSingleton() {
instance = new TestClass();
}
public TestClass getInstance() {
return instance;
}
}
不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象
**单元素的枚举类型已经成为实现Singleton的最佳方法**。
五、单例模式的应用场景
当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 Web 中的配置对象、数据库的连接池等。当某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。
Spring ioc 容器的bean 都是默认单例的,即spring 依赖注入Bean 实例默认都是单例的。