Java 单例模式详解
本文内容纲要:
概念:
java中单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例、饿汉式单例、登记式单例三种。
单例模式有一下特点:
1、单例类只能有一个实例。
2、单例类必须自己自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个PrinterSpooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头。
首先看一个经典的单例实现。
publicclassSingleton{
privatestaticSingletonuniqueInstance=null;
privateSingleton(){
//Existsonlytodefeatinstantiation.
}
publicstaticSingletongetInstance(){
if(uniqueInstance==null){
uniqueInstance=newSingleton();
}
returnuniqueInstance;
}
//Othermethods...
}
Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。(事实上,**通过Java反射机制是能够实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效。**此问题在此处不做讨论,姑且掩耳盗铃地认为反射机制不存在。)
但是以上实现没有考虑线程安全问题。所谓线程安全是指:如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。显然以上实现并不满足线程安全的要求,在并发环境下很可能出现多个Singleton实例。
1publicclassTestStream{
2privateStringname;
3publicStringgetName(){
4returnname;
5}
6publicvoidsetName(Stringname){
7this.name=name;
8}
9//该类只能有一个实例
10privateTestStream(){}//私有无参构造方法
11//该类必须自行创建
12//有2种方式
13/*privatestaticfinalTestStreamts=newTestStream();*/
14privatestaticTestStreamts1=null;
15//这个类必须自动向整个系统提供这个实例对象
16publicstaticTestStreamgetTest(){
17if(ts1==null){
18ts1=newTestStream();
19}
20returnts1;
21}
22publicvoidgetInfo(){
23System.out.println("outputmessage"+name);
24}
25}
1publicclassTestMain{
2publicstaticvoidmain(String[]args){
3TestStreams=TestStream.getTest();
4s.setName("张孝祥");
5System.out.println(s.getName());
6TestStreams1=TestStream.getTest();
7s1.setName("张孝祥");
8System.out.println(s1.getName());
9s.getInfo();
10s1.getInfo();
11if(s==s1){
12System.out.println("创建的是同一个实例");
13}elseif(s!=s1){
14System.out.println("创建的不是同一个实例");
15}else{
16System.out.println("applicationerror");
17}
18}
19}
运行结果:
张孝祥
张孝祥
outputmessage张孝祥
outputmessage张孝祥
创建的是同一个实例
结论:由结果可以得知单例模式为一个面向对象的应用程序提供了对象惟一的访问点,不管它实现何种功能,整个应用程序都会同享一个实例对象。
1.饿汉式单例类
1//饿汉式单例类.在类初始化时,已经自行实例化
2publicclassSingleton1{
3//私有的默认构造子
4privateSingleton1(){}
5//已经自行实例化
6privatestaticfinalSingleton1single=newSingleton1();
7//静态工厂方法
8publicstaticSingleton1getInstance(){
9returnsingle;
10}
11}
2.懒汉式单例类
1//懒汉式单例类.在第一次调用的时候实例化
2publicclassSingleton2{
3//私有的默认构造子
4privateSingleton2(){}
5//注意,这里没有final
6privatestaticSingleton2single=null;
7//静态工厂方法
8publicsynchronizedstaticSingleton2getInstance(){
9if(single==null){
10single=newSingleton2();
11}
12returnsingle;
13}
14}
3.登记式单例类
1importjava.util.HashMap;
2importjava.util.Map;
3//登记式单例类.
4//类似Spring里面的方法,将类名注册,下次从里面直接获取。
5publicclassSingleton3{
6privatestaticMap<String,Singleton3>map=newHashMap<String,Singleton3>();
7static{
8Singleton3single=newSingleton3();
9map.put(single.getClass().getName(),single);
10}
11//保护的默认构造子
12protectedSingleton3(){}
13//静态工厂方法,返还此类惟一的实例
14publicstaticSingleton3getInstance(Stringname){
15if(name==null){
16name=Singleton3.class.getName();
17System.out.println("name==null"+"--->name="+name);
18}
19if(map.get(name)==null){
20try{
21map.put(name,(Singleton3)Class.forName(name).newInstance());
22}catch(InstantiationExceptione){
23e.printStackTrace();
24}catch(IllegalAccessExceptione){
25e.printStackTrace();
26}catch(ClassNotFoundExceptione){
27e.printStackTrace();
28}
29}
30returnmap.get(name);
31}
32//一个示意性的商业方法
33publicStringabout(){
34return"Hello,IamRegSingleton.";
35}
36publicstaticvoidmain(String[]args){
37Singleton3single3=Singleton3.getInstance(null);
38System.out.println(single3.about());
39}
40}
本文内容总结:
原文链接:https://www.cnblogs.com/whgw/archive/2011/10/05/2199535.html