目录
一、单例模式的定义和作用
设计模式:是软件开发人员在软件开发过程中面临的一般问题的解决方案
目的
- 使得类的一个对象成为该类系统中的唯一实例
定义
- 一个类有且仅有一个实例,并且自行实例化想整个系统提供
要点
- 某个类只能有一个实例
- 必须自行创建实例
- 必须自行向整个系统提供这个实例
实现:
- 只提供私有的构造方法(私有构造方法,类外无法通过new关键字来实例化对象)
- 含有一个该类的静态私有对象(static刚好可以满足有且仅有)
- 提供一个静态的公有方法用于创建、获取静态私有对象
代码实现方案
- 饿汉式:对象创建过程中实例化
- 懒汉式:静态共有方法中实例化
饿汉式实现方式,用慕课网的作业来说明
构造方法中有一条打印语句,实例化了多少个对象就会输出几次,但是我们发现最终只有一条打印输出,这就是static关键字的作用,在类加载的时候执行private static Earth instance = new Earth();语句,该语句还会负责实例化对象(地球诞生就是这时候打印的),并让instance指向实例化出来的对象,static属性只会在类加载的时候加载,也即该语句只会被调用一次。加之构造方法是私有的,在类外无法进行对象的实例化,在类内部也只有一个实例化语句还是static修饰的,故只会有一个对象。
//饿汉式:创建对象实例的时候直接初始化,空间换时间
public class Earth {
//定义私有构造方法,并在构造方法中打印输出“地球诞生”
private Earth(){
System.out.println("地球诞生");
}
//定义私有静态类对象并完成实例化
private static Earth instance = new Earth();
//定义公有静态方法返回类内的私有静态对象
public static Earth getInstance(){
return instance;
}
}
我们来看看测试类,以及测试的输出
public class EarthTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("第一个地球创建中。。。。");
Earth one = Earth.getInstance();
System.out.println("第二个地球创建中。。。。");
Earth two = Earth.getInstance();
System.out.println("问:两个地球是同一个么?");
System.out.println(one);
System.out.println(two);
System.out.println(one == two);
}
}
输出,从输出可以看出,构造方法只执行了一次,且从后面one和two的打印来看指向的是同一个对象。
第一个地球创建中。。。。
地球诞生
第二个地球创建中。。。。
问:两个地球是同一个么?
com.imooc.earth.Earth@6e0be858
com.imooc.earth.Earth@6e0be858
true
下面我们来看看懒汉式
这边与饿汉式的区别是类内实例对象创建时并不直接初始化,直到第一次调用get方法时,才会完成初始化操作,然后我们通过get方法的判断语句确保只有一个实例对象。
// 懒汉式:类内实例对象创建时并不直接初始化,直到第一次调用get方法时,才会完成初始化操作
// 时间换空间
public class SingletonTwo {
//1、创建私有构造方法
private SingletonTwo(){
System.out.println("我是SingletonTwo的构造方法");
}
//2、创建静态的该类实例对象
private static SingletonTwo instance = null;
//3、创建开放的静态方法提供实例对象
public static SingletonTwo getInstance(){
// 精髓,这边保证了只有一个实例
if(instance == null)
instance = new SingletonTwo();
return instance;
}
}
public class SingletonTest {
public static void main(String[] args){
SingletonTwo singletonTwoOne = SingletonTwo.getInstance();
System.out.println(singletonTwoOne);
SingletonTwo singletonTwoTwo = SingletonTwo.getInstance();
System.out.println(singletonTwoTwo);
System.out.println(singletonTwoOne == singletonTwoTwo);
}
}
输出,从输出我们依旧可以看出,他们指向的是同一块内存空间。
我是SingletonTwo的构造方法
com.imooc.singleton.SingletonTwo@61bbe9ba
com.imooc.singleton.SingletonTwo@61bbe9ba
true
- 饿汉式线程安全;懒汉式存在线程风险
懒汉式线程风险解决方法
- 同步锁
- 双重教验锁
- 静态内部类
- 枚举
等方式来保证操作时的线程唯一。
单例模式优缺点总结
单例模式优点
- 在内存中只有一个对象,节省内存空间
- 避免频繁的创建销毁对象,提高性能
- 避免对共享资源的多重占用
单例模式缺点
- 扩展比较困难
- 若实例化的对象长期不利用,系统将默认为垃圾进行回收,造成对象状态丢失
单例模式使用场景
- 创建对象时占用资源过多,但同时又需要用到该类对象
- 对系统内资源要求统一读写,如读写配置信息
- 当多个实例存在可能引起程序逻辑错误,如号码生成器
关于单例模式的一些问题的说明
所谓单例模式只能有一个对象,指无论应用该类型产生多少对象,都是指向唯一的堆空间。
关于getInstance方法前的static的说明,对于单例模式,需要在类外能在无需产生对象实力的情况下,完成这个方法的调用,因此该方法需要设置为static,这样就可以在类外通过类名调用。否则必须先实例化对象,通过对象名调用,但是由于构造方法的private,在类外无法通过new关键字实例化对象。产生矛盾。
关于final
- 懒汉式不能加入final,因为final修饰的静态成员,若定义时未初始化,不能在类方法中进行实例初始化。
- 饿汉式,对类实例只涉及创建和返回,则可以加入;若还包括其他功能,如资源回收等,加入final有可能会影响程序功能实现。