设计模式有很多种,单例模式只是其中一种,很多面试官都会考,但是怎么写出高水平的单例模式呢?今天给大家分享下。
首先单例模式保证类的对象在内存中全局唯一性,主要是为了减少对象对资源的占用。
那么要怎么实现呢?问题有2个,一是类的外部不允许直接构建此类对象,二是类的外部只能通过静态方法访问此对象。解决方法:首先是构造方法私有化,在类的内部构建对象,通过定义一个静态方法,通过这个方法直接返回此对象。
class Singleton01{
private Singleton01(){}
private static Singleton01 instance;
public static Singleton01 getInstance(){
if(instance==null){
instance=new Singleton01();
}
return instance;
}
}
但是这个方法存在线程安全问题,如果多个线程同时访问getInstance()方法,
可能会创建多个对象,那么该怎么办呢?
可以使用synchronized关键字,保证在某个时间只有一个线程能访问getInstance()
方法。
class Singleton02{
private Singleton02(){}
private static Singleton02 instance;
public static synchronized Singleton02 getInstance(){
if(instance==null){
instance=new Singleton02();
}
return instance;
}
}
这个虽然解决了线程安全问题,但是如果多个线程访问getInstance()方法,
只有一个线程执行,其他线程全部阻塞,会极大的影响性能。
熟悉synchronized的小伙伴都知道,synchronized方法不仅可用于修饰方法,
还可作用于静态方法和代码块。
class Singleton03{
private Singleton03(){}
private static volatile Singleton03 instance;
public static Singleton03 getInstance(){
if(instance==null){
synchronized(Singleton03.class){
if(instance==null){
instance=new Singleton03();
}
}
}
return instance;
}
}
这一行代码 instance=new Singleton03();其实是3个步骤,
首先为instance分配内存空间,然后初始化instance,最后将instance指向分配的
内存空间,但是在jvm中会为了提高效率进行指令重排序。因此可能会得到一个没有
实例化的instance对象,使用volatile声明对象,防止指令重排序。
但是不适合频繁访问。
class Singleton04{
private Singleton04(){}
//类加载时创建对象
private static final Singleton04 instance=new Singleton04();
//没有阻塞问题适合频繁访问
public static Singleton04 getInstance(){
return instance;
}
}
这个单例设计适合频繁访问,如果对象比较大,在类加载的时候就创建了此对象,
但是假如不适用,就可能长时间占用。我们可以使用延迟加载策略。
class Singleton05{
private Singleton05(){}
static class Lazy{//只有使用这个内部类,才会被加载
//使用外部类的成员方法,成员变量时,只加载外部类,这个内部类不被加载
public static final Singleton05 instance=new Singleton05();
}
public static Singleton05 getInstance(){
//何时需要何时加载
return Lazy.instance;
}
}
单例模式你学会了吗?