Java 单例模式详解(转)

概念:
  java中单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例、饿汉式单例、登记式单例三种。
  单例模式有一下特点:
  1、单例类只能有一个实例。
  2、单例类必须自己自己创建自己的唯一实例。
  3、单例类必须给所有其他对象提供这一实例。
  单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头。

首先看一个经典的单例实现。

public class Singleton {

private static Singleton uniqueInstance = null;

private Singleton() {

// Exists only to defeat instantiation.

}

public static Singleton getInstance() {

if (uniqueInstance == null) {

uniqueInstance = new Singleton();

}

return uniqueInstance;

}

// Other methods...

}

Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。(事实上,通过Java反射机制是能够实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效。此问题在此处不做讨论,姑且掩耳盗铃地认为反射机制不存在。)

但是以上实现没有考虑线程安全问题。所谓线程安全是指:如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。显然以上实现并不满足线程安全的要求,在并发环境下很可能出现多个Singleton实例。

 1 public class TestStream { 2     private String name; 3     public String getName() { 4         return name; 5     } 6     public void setName(String name) { 7         this.name = name; 8     }  9     //该类只能有一个实例10     private TestStream(){}    //私有无参构造方法11     //该类必须自行创建12     //有2种方式13     /*private static final TestStream ts=new TestStream();*/14     private static TestStream ts1=null;15     //这个类必须自动向整个系统提供这个实例对象16     public static TestStream getTest(){17         if(ts1==null){18             ts1=new TestStream();19         }20         return ts1;21     }22     public void getInfo(){23         System.out.println("output message "+name);24     }25 }

 1 public class TestMain { 2     public static void main(String [] args){ 3         TestStream s=TestStream.getTest(); 4         s.setName("张孝祥"); 5         System.out.println(s.getName()); 6         TestStream s1=TestStream.getTest(); 7         s1.setName("张孝祥"); 8         System.out.println(s1.getName()); 9         s.getInfo();10         s1.getInfo();11         if(s==s1){12             System.out.println("创建的是同一个实例");13         }else if(s!=s1){14             System.out.println("创建的不是同一个实例");15         }else{16             System.out.println("application error");17         }18     }19 }

运行结果:
  张孝祥
  张孝祥
  output message 张孝祥
  output message 张孝祥
  创建的是同一个实例

结论:由结果可以得知单例模式为一个面向对象的应用程序提供了对象惟一的访问点,不管它实现何种功能,整个应用程序都会同享一个实例对象。

1.饿汉式单例类

 1 //饿汉式单例类.在类初始化时,已经自行实例化  2 public class Singleton1 { 3     //私有的默认构造子 4     private Singleton1() {} 5     //已经自行实例化  6     private static final Singleton1 single = new Singleton1(); 7     //静态工厂方法  8     public static Singleton1 getInstance() { 9         return single;10     }11 }

2.懒汉式单例类

 1 //懒汉式单例类.在第一次调用的时候实例化  2 public class Singleton2 { 3     //私有的默认构造子 4     private Singleton2() {} 5     //注意,这里没有final     6     private static Singleton2 single=null; 7     //静态工厂方法  8     public synchronized  static Singleton2 getInstance() { 9          if (single == null) {  10              single = new Singleton2();11          }  12         return single;13     }14 }

3.登记式单例类

 1 import java.util.HashMap; 2 import java.util.Map; 3 //登记式单例类. 4 //类似Spring里面的方法,将类名注册,下次从里面直接获取。 5 public class Singleton3 { 6     private static Map<String,Singleton3> map = new HashMap<String,Singleton3>(); 7     static{ 8         Singleton3 single = new Singleton3(); 9         map.put(single.getClass().getName(), single);10     }11     //保护的默认构造子12     protected Singleton3(){}13     //静态工厂方法,返还此类惟一的实例14     public static Singleton3 getInstance(String name) {15         if(name == null) {16             name = Singleton3.class.getName();17             System.out.println("name == null"+"--->name="+name);18         }19         if(map.get(name) == null) {20             try {21                 map.put(name, (Singleton3) Class.forName(name).newInstance());22             } catch (InstantiationException e) {23                 e.printStackTrace();24             } catch (IllegalAccessException e) {25                 e.printStackTrace();26             } catch (ClassNotFoundException e) {27                 e.printStackTrace();28             }29         }30         return map.get(name);31     }32     //一个示意性的商业方法33     public String about() {    34         return "Hello, I am RegSingleton.";    35     }    36     public static void main(String[] args) {37         Singleton3 single3 = Singleton3.getInstance(null);38         System.out.println(single3.about());39     }40 }
时间: 2024-10-13 11:30:57

Java 单例模式详解(转)的相关文章

9种Java单例模式详解(推荐)

单例模式的特点 一个类只允许产生一个实例化对象. 单例类构造方法私有化,不允许外部创建对象. 单例类向外提供静态方法,调用方法返回内部创建的实例化对象.  懒汉式(线程不安全) 其主要表现在单例类在外部需要创建实例化对象时再进行实例化,进而达到Lazy Loading 的效果. 通过静态方法 getSingleton() 和private 权限构造方法为创建一个实例化对象提供唯一的途径. 不足:未考虑到多线程的情况下可能会存在多个访问者同时访问,发生构造出多个对象的问题,所以在多线程下不可用这种

Java 单例模式详解

概念: java中单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例.饿汉式单例.登记式单例三种. 单例模式有一下特点: 1.单例类只能有一个实例. 2.单例类必须自己自己创建自己的唯一实例. 3.单例类必须给所有其他对象提供这一实例. 单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例.在计算机系统中,线程池.缓存.日志对象.对话框.打印机.显卡的驱动程序对象常被设计成单例.这些应用都或多或少具有资源管理器的功能.每台计算机可以有若干个打印机,但只能有一个Printe

【JAVA单例模式详解】

设计模式是一种思想,适合于任何一门面向对象的语言.共有23种设计模式. 单例设计模式所解决的问题就是:保证类的对象在内存中唯一. 举例: A.B类都想要操作配置文件信息Config.java,所以在方法中都使用了Config con=new Config();但是这是两个不同的对象.对两者的操作互不影响,不符合条件. 解决思路: 1.不允许其他程序使用new创建该类对象.(别人new不可控)2.在该类中创建一个本类实例.3.对外提供一个方法让其他程序可以获取该对象. 解决方法:单例模式. 步骤:

java单例模式详解[转载]

概念: java中单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例.饿汉式单例.登记式单例三种. 单例模式有一下特点: 1.单例类只能有一个实例. 2.单例类必须自己自己创建自己的唯一实例. 3.单例类必须给所有其他对象提供这一实例. 单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例.在计算机系统中,线程池.缓存.日志对象.对话框.打印机.显卡的驱动程序对象常被设计成单例.这些应用都或多或少具有资源管理器的功能.每台计算机可以有若干个打印机,但只能有一个Printe

java单例模式详解

1.懒汉模式: 特点:lazy loading很明显,也就是在需要的时候才加载,也就是我们常说的延迟加载. (1)线程不安全: public class Singleton { private static Singleton instance; public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } (2)线程安全: pu

Java堆栈详解 .

1. Java中堆栈(stack)和堆(heap) (1)内存分配的策略 按照编译原理的观点,程序运行时的内存分配有三种策略,分别是静态的,栈式的,和堆式的. 静态存储分配是指在编译时就能确定每个数据目标在运行时刻的存储空间需求,因而在编译时就可以给他们分配固定的内存空间.这种分配策略要求程序代码中不 允许有可变数据结构(比如可变数组)的存在,也不允许有嵌套或者递归的结构出现,因为它们都会导致编译程序无法计算准确的存储空间需求.栈式存储分配也可称为动态存储分配,是由一个类似于堆栈的运行栈来实现的

Java 多线程详解(二)------如何创建进程和线程

Java 多线程详解(一)------概念的引入:http://www.cnblogs.com/ysocean/p/6882988.html 在上一篇博客中,我们已经介绍了并发和并行的区别,以及进程和线程的理解,那么在Java 中如何创建进程和线程呢? 1.在 Windows 操作系统中创建进程 在 windows 操作系统中,我们创建一个进程通常就是打开某个应用软件,这便在电脑中创建了一个进程.更原始一点的,我们在命令提示符中来做(我们以打开记事本这个进程为例): 第一步:windows+R,

package-info.java文件详解

package-info.java文件详解 作者:chszs,转载需注明.博客主页:http://blog.csdn.net/chszs 一.pacakge-info.java介绍 pacakge-info.java是一个Java文件,可以添加到任何的Java源码包中.pacakge-info.java的目标是提供一个包级的文档说明或者是包级的注释. pacakge-info.java文件中,唯一要求包含的内容是包的声明语句,比如: package com.ch.service; 二.包文档 在

java 反射 详解

本文来自:blog.csdn.net/ljphhj JAVA反射机制:   通俗地说,反射机制就是可以把一个类,类的成员(函数,属性),当成一个对象来操作,希望读者能理解,也就是说,类,类的成员,我们在运行的时候还可以动态地去操作他们. 理论的东东太多也没用,下面我们看看实践 Demo - Demo: package cn.lee.demo; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import