java单例模式singleton回顾,如何实现单例模式

最近面试,用到了单例模式相关的知识,这里总结一下,温故知新。


单例模式,是java开发中经常会用到的一种模式。通常指的是在程序的执行过程中,只产生一个实例对象。单例模式通常有以下几种应用场景:

1,硬性需求,比如打印机的打印功能;

2,减小系统开销,比如在程序运行的过程中,可能会频繁的创建某一个类的实例,采用单例模式可以只创建一次该类的实例,从而降低系统开销。


通过以上的分析,我们可以大致总结出单例模式(singleton)的几个特点:

1,全局的,所以要用stat ic修饰

2,私有的构造方法和私有的类对象,这样确保外部类没法通过new一个对象或者直接调用类对象

3,暴露一个公共的成员方法,供外部类调用


我们说,无论什么样的单例模式,都需要结合实际的情况来设计。所以就产生了很多单例模式的实现方法,大概有以下几种:

1,懒汉式单例模式 

2,饿汉式单例模式

3,线程安全的单例模式

4,静态内部类单例模式

5,枚举单例模式


1,懒汉式单例模式 

懒汉式单例模式可以实现对象的延迟加载,减少系统负载。按照这几个要点,我们可以设计这样的一个类SingleTon

public class SingleTon {
	/**
	 * 设计一个类实现singleton模式
	 * 
	 * 三个要点:
	 * 1,全局的,所以要用static关键字修饰
	 * 2,私有的构造方法和私有的类对象,这样确保外部类没法通过new一个对象或者直接调用类对象
	 * 3,暴露一个公共的成员方法,供外部类调用
	 * 
	 */
	
	//1,私有的构造方法
	private SingleTon(){
		
	}
	
	//2,私有的、静态 的类对象
	private static SingleTon singleTonInstance = null;
	
	//3,暴露一个公共的成员方法,供外部类调用
	public static SingleTon getSingleTonInstance(){
		
		//判断是否创建了实例
		if(singleTonInstance == null){
			
			//创建实例
			singleTonInstance = new SingleTon();
			
		}
		
		return singleTonInstance;
		
	}

}



作为对比,我们再写一个普通的java类:
<span style="font-size:14px;">public class NotSingleTon {
	
	//1,作为对比,这里把构造方法,改成公共的
	public NotSingleTon(){
	
	}
	
}</span>

然后我们来测试一下:


<span style="font-size:14px;">package com.java.test;

public class SingleTonTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		//单例模式测试
		//static类型的成员方法是属于类的,所以建议使用直接使用类名调用
		SingleTon singleTonInstance_1 = SingleTon.getSingleTonInstance();
		SingleTon singleTonInstance_2 = SingleTon.getSingleTonInstance();
		//测试输出
                System.out.println("singleTonInstance_1.hashCode() = " + singleTonInstance_1.hashCode());
                System.out.println("singleTonInstance_2.hashCode() = " + singleTonInstance_2.hashCode());
        
                //非单例模式测试
                NotSingleTon notsingleTonInstance_1 = new NotSingleTon();
                NotSingleTon notsingleTonInstance_2 = new NotSingleTon();
        
                //测试输出
                System.out.println("notsingleTonInstance_1.hashCode() = " + notsingleTonInstance_1.hashCode());
                System.out.println("notsingleTonInstance_2.hashCode() = " + notsingleTonInstance_2.hashCode());

	}

}
</span>

打印台输出结果:
<span style="font-size:14px;">singleTonInstance_1.hashCode() = 29115481
singleTonInstance_2.hashCode() = 29115481
notsingleTonInstance_1.hashCode() = 4872882
notsingleTonInstance_2.hashCode() = 25724761
</span>


上面这个写法,是懒汉式单例模式。优点是可以实现对象的延迟加载,减少系统开销。缺点是存在线程安全问题,如果在多线程环境下,可能重复创建对象。


2,饿汉式单例模式

下面我们来看一下饿汉式单例模式:

public class SingleTon {
	
	//1,私有的构造方法
	private SingleTon(){
		
	}
	
	//2,全局的类对象
	private static SingleTon singleTonInstance = new SingleTon();
	
	//3,暴露一个公共的成员方法,供外部类调用
	public static SingleTon getSingleTonInstance(){
		
	    return singleTonInstance;
		
	}

}

这就是我们通常所说的饿汉式单例模式,在首次加载实例时,就创建实例的对象,而不管实际是否需要马上创建这个对象。


3,线程安全的单例模式

在多线程环境下,线程安全是一个必须要考虑的问题。

public class SingleTon {
<pre name="code" class="java" style="font-size: 14px;">    //<span style="font-family: Arial, Helvetica, sans-serif;">私有的构造方法</span>
    private SingleTon(){
    }


    //私有的、静态的、volatile的SingleTon对象
    private static volatile SingleTon singleTonInstance = null;
    
    public static SingleTon getSingleTonInstance(){
        if(singleTonInstance == null){
            synchronized (SingleTon.class){
                if(singleTonInstance == null){
                    singleTonInstance = new SingleTon();
                }
            }
        }
        return singleTonInstance;
    }    
}

4,静态内部类单例模式

我们知道,静态内部类只会被加载一次,所以是线程安全的。

public class SingleTon {
    //静态内部类
    private static class Holder {
        private static SingleTon singleTonInstance = new SingleTon();
    }
    //私有的构造方法
    private SingleTon(){
    }
        
    public static SingleTon getSingleTonInstance(){
        return Holder.singleTonInstance;
    }
}

上面这4种方法,实现的单例模式,都有一个共同的缺陷,就是无法避免java的反射机制,创建新的对象。而只有枚举类,才能避免反射机制创建新的的对象。


好了,今天暂时聊到这里,改天再叙。





你可能感兴趣的:(java,面试)