实现一个单例对象的方法分三步:
<1>私有化本类的构造方法
<2>创建本类对象
<3>提供对外公共的访问方法,可以让外类访获取到该单例对象(也就是提供一个 get 方法)
例如:
package Singleton; public class Singleton_test { public static void main(String[] args) { //这里可以获取该单例对象 Singleton.getInstance(); } } //饿汉式的单例模式,上来就把实例对象创建出来 class Singleton{ //私有化构造方法,可以保证在类外该类不能被实例化 private Singleton(){} //创建该类的对象,并且用 private 修饰,保证外类不能对该对象赋值 //创建单例的目的就是确报下面创建的对象不被更改 private static Singleton s = new Singleton(); //提供静态的 get 方法,保证外类直接类名.方法获取该对象 public static Singleton getInstance(){ return s; } } //懒汉式的单例模式 //只有在使用的时候才创建该类实例 class Singleton { private Singleton(){}; private static Singleton s; public static Singleton getInstance(){ if(s==null)//判断s是否为空,如果为空,创建该类实例,否则直接返回s s=new Singleton(); return s; } }
对于饿汉式还有一种创建方法:上面那种是用的比较多的也是比较官方的创建单例模式的饿汉式方法,由于我们创建单例模式的目的是该实例对象不被更改,所以我们在前面加上一个 final 关键字修饰一下也可以,这三种方式都需要首先私有化该类的构造方法
//饿汉式的"民间"创建方法 class Singleton{ private Singleton(){} public final static Singleton s = new Singleton(); }
饿汉式和懒汉式的优缺点:
在单线程的情况下:
饿汉式是用空间换时间,即:饿汉式上来就创建该单例对象,用的时候直接拿来用,节省了创建对象的时间,但是浪费了内存空间资源
懒汉式是用时间换空间,即:懒汉式在用到该单例对象的时候才创建,节省了内存空间,但是在时间上有延迟,因此懒汉式也叫延迟加载
在多线程情况下:
饿汉式是线程安全的
懒汉式有时候会存在安全隐患,比如有两个线程在调用该单例对象,线程1在执行到 if 语句的判断条件后(还没执行 new Singleton()这个语句),被其他线程抢走了执行权(比如 yield,priority 等限制的时候),恰好在此时,线程2进来了,此时的 s 仍然满足为 null 的条件,所以创建了一个实例对象,但是线程1在等到其他线程执行完之后,又继续执行实例对象的创建,导致创建了两个实例,在计算机硬件条件越来越发达的今天,浪费点内存已经不算什么了,但是时间是宝贵的,时间是不会增加的,所以我们一般选择“饿汉式”的方法设计单例模式