Java 单例模式几种实现的差别
来源:互联网 发布:unix环境高级编程源码 编辑:程序博客网 时间:2024/06/05 18:52
单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
思路是通过将该类的构造方法设为private,那么在其他类中不能直接实例化该类。那么为了得到该类的实例,需要有public的static方法来返回该类的实例。(之所以static是为了可以直接用类名来调用该方法,不然的话没有该类的实例,无法调用该类的非static方法)
单例模式有以下特点:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
单例模式主要有以下三种实现方式:懒汉式单例、饿汉式单例、登记式单例。下面来看代码示例:
一、懒汉式单例
//懒汉式单例类.在第一次调用的时候实例化自己 public class Singleton { private Singleton() {} private static Singleton single=null; //静态工厂方法 public static Singleton getInstance() { if (single == null) { single = new Singleton(); } return single; }}
懒汉式其实是一种比较形象的称谓。既然懒,那么在创建对象实例的时候就不着急。会一直等到马上要使用对象实例的时候才会创建,懒人嘛,总是推脱不开的时候才会真正去执行工作,因此在装载对象的时候不创建对象实例。懒汉式是典型的时间换空间,就是每次获取实例都会进行判断,看是否需要创建实例,浪费判断的时间。当然,如果一直没有人使用的话,那就不会创建实例,则节约内存空间。
//饿汉式单例类.在类初始化时,已经自行实例化 public class Singleton1 { private Singleton1() {} private static final Singleton1 single = new Singleton1(); //静态工厂方法 public static Singleton1 getInstance() { return single; }}
饿汉式也是一种比较形象的称谓。既然饿,那么在创建对象实例的时候就比较着急,饿了嘛,于是在装载类的时候就创建对象实例。饿汉式是典型的空间换时间,当类装载的时候就会创建类的实例,不管你用不用,先创建出来,然后每次调用的时候,就不需要再判断,节省了运行时间。
三、登记式单例
//类似Spring里面的方法,将类名注册,下次从里面直接获取。public class Singleton3 { private static Map<String,Singleton3> map = new HashMap<String,Singleton3>(); static{ Singleton3 single = new Singleton3(); map.put(single.getClass().getName(), single); } //保护的默认构造子 protected Singleton3(){} //静态工厂方法,返还此类惟一的实例 public static Singleton3 getInstance(String name) { if(name == null) { name = Singleton3.class.getName(); System.out.println("name == null"+"--->name="+name); } if(map.get(name) == null) { try { map.put(name, (Singleton3) Class.forName(name).newInstance()); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } return map.get(name); } //一个示意性的商业方法 public String about() { return "Hello, I am RegSingleton."; } public static void main(String[] args) { Singleton3 single3 = Singleton3.getInstance(null); System.out.println(single3.about()); }}登记式单例实际上维护了一组单例类的实例,将这些实例存放在一个Map(登记薄)中,对于已经登记过的实例,则从Map直接返回,对于没有登记的,则先登记,然后返回。
线程安全问题
上文中懒汉式单例的代码是线程不安全的。为了线程安全,可以采用以下方法:
1、在getInstance方法上加synchronized
public static synchronized Singleton getInstance() { if (single == null) { single = new Singleton(); } return single;}2、双重检查锁定
public static Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; }3、静态内部类
public class Singleton { private Singleton(){} /** * 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例 * 没有绑定关系,而且只有被调用到时才会装载,从而实现了延迟加载。 */ private static class SingletonHolder{ /** * 静态初始化器,由JVM来保证线程安全 */ private static Singleton instance = new Singleton(); } public static Singleton getInstance(){ return SingletonHolder.instance; }}
当getInstance方法第一次被调用的时候,它第一次读取SingletonHolder.instance,导致SingletonHolder类得到初始化;而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建Singleton的实例,由于是静态的域,因此只会在虚拟机装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。
这个模式的优势在于,getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。
另外就静态内部类中的三个static说明如下:
public static Singleton getInstance(){return SingletonHolder.instance;}中的static是为了可以用类名直接调用getInstance方法,所以必须为static。那么该方法内的成员变量也必须为static,否则那个成员变量就要用类的实例来访问了,这显然和通过类直接访问不相符。
private static Singleton instance = new Singleton();中的static是因为instance必须为static才可以在getInstance方法中出现。
而
public static Singleton getInstance()
中的static是因为若内部类中出现静态成员变量,则该内部类必为静态内部类。
最后来看一个单例模式应用的例子有助于理解:
public class TMain{public static void main(String[] args){Singleton s1=Singleton.getInstance();s1.setName("libai");Singleton s2=Singleton.getInstance();s2.setName("dufu");System.out.println(s1.getName());System.out.println(s2.getName());if(s1==s2){System.out.println("是同一个对象");}else{System.out.println("不是同一个对象");}}}class Singleton{ /* 私有构造方法,防止被实例化 */ private Singleton() { } private String name; public void setName(String s){ this.name=s; } public String getName(){ return this.name; } /*饿汉式单例*/ private static final Singleton instance = new Singleton(); /* 获取实例 */ public static Singleton getInstance() { //return new Singleton(); return instance; } /* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */ public Object readResolve() { return getInstance(); } }
输出为:
dufudufu是同一个对象
- Java 单例模式几种实现的差别
- Java 单例模式几种实现的差别
- java单例模式的几种实现
- JAVA单例模式的几种实现方法
- JAVA单例模式的几种实现方法
- JAVA单例模式的几种实现方法
- JAVA单例模式的几种实现方法
- JAVA单例模式的几种实现方法
- JAVA单例模式的几种实现方法
- JAVA单例模式的几种实现方法
- JAVA单例模式的几种实现方法
- java实现单例singleton的几种模式
- Java -- 单例模式的几种实现方法
- JAVA单例模式的几种实现方法
- Java单例模式的几种实现方式
- java单例模式的几种不同实现
- java实现单例模式的几种方式
- java 单例模式的几种实现
- js 与或运算符 || && 妙用
- 结构体、结构指针作为函数参数
- mysql优化sql语句步骤
- STL — 关联容器
- 命令行(The Command Line is Your Best Friend)
- Java 单例模式几种实现的差别
- 87 thinkphp 和sql查询条件为某字段不为空的情况
- Shell命令——文件目录
- CentOS 7 yum安装 Mono 和 手动 安装Jexus
- C++入门经典 笔记 (第十六章)使用继承扩展类
- Android proguard 详解(二)
- leetcode: (226) Invert Binary Tree
- 大二暑假
- POJ 3191 The Moronic Cowmpouter