单例模式简介
单例模式是软件设计模式中最简单的一种设计模式。从名称中可以看出,单例的目的就是使系统中只能包含有一个该类的唯一一个实例。
单例模式最初的定义出现于《设计模式》(艾迪生维斯理, 1994):“保证一个类仅有一个实例,并提供一个访问它的全局访问点。”
Java中单例模式定义:“一个类有且仅有一个实例,并且自行实例化向整个系统提供。”
对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。如在Windows中就只能打开一个任务管理器。如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源;如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态。因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要。
本文将以Java语言来描述单例类的几种实现方式,以及分析给出的几种实现方式的优缺点和并用程序验证性能数据。
Java实例化的方式
我们知道在Java语言中创建实例的方法大体上有4种:
1、使用new关键字
import java.io.Serializable; import java.util.Random; /** * @author thinkpad * */ public class Singleton implements Serializable{ private static final long serialVersionUID = -3868390997950184335L; private static Random random = new Random(); private int id; private String name; private int [] data = { random.nextInt(), random.nextInt(), random.nextInt() }; //default constructor public Singleton(){ } //public constructor public Singleton(int id){ this.id = id * 2; } //private constructor private Singleton(int id,String name){ this(id); this.name = name + "." + name; } @Override public String toString(){ StringBuffer buf = new StringBuffer(); buf.append(super.toString()).append("\n") .append("id :").append(id).append("\n") .append("name :").append(this.name).append("\n") .append("data array :").append(data.toString()); return buf.toString(); } }
最常见的使用new 关键字,创建一个对象
public class TestSingleton { public static void main(String[] args) { Singleton single = new Singleton(1); } }
2、使用反射
使用反射能够突破JAVA中可见权限,本示例中调用了Singleton私有的构造方法。
import java.lang.reflect.Constructor; import java.lang.reflect.Type; /** * @author thinkpad * */ public class TestSingletonUseReflect { public static void main(String[] args) { try { Constructor<?>[] constructors = Singleton.class.getDeclaredConstructors(); System.out.println(constructors.length); for(Constructor<?> con : constructors ) { Type[] types = con.getParameterTypes(); if (types.length == 2){ con.setAccessible(true); Singleton singleton = null; singleton = (Singleton) con.newInstance(1,"single1"); System.out.println(singleton); Singleton singleton2 = null; singleton2 = (Singleton) con.newInstance(2,"single2"); System.out.println(singleton2); } } }catch(Exception e){ e.printStackTrace(); } } }
3、使用反序列化
我们知道Java对象序列化(Object Serialization)将那些实现了Serializable接口的对象转换成一个字节序列,并可以通过反序列化将这个字节序列完全恢复成为原来的对象,因此反序列化提供了一个创建对象的方式。由于使用反序列化创建对象和使用new关键字创建对象有一些不同,反序列化过程中构造方法是没有被调用的(也不一定,若序列化对象父类没有实现Serializable接口,例如Object类,序列化过程中会递归调用父类的无参构造函数),而且其中的域的初始化代码也没有被执行。
import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; /** * @author thinkpad * */ public class TestSerializable { /** * @param args */ public static void main(String[] args) { Singleton single1 = new Singleton(100); Singleton single2 = new Singleton(200); try { ByteArrayOutputStream buf = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(buf); System.out.println("**********begin serializable**********"); System.out.println(single1); System.out.println(single2); out.writeObject(single1); out.writeObject(single2); out.close(); System.out.println("**********begin unserializable**********"); ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buf.toByteArray())); Singleton s1 = (Singleton)in.readObject(); Singleton s2 = (Singleton)in.readObject(); System.out.println(s1.toString()); System.out.println(s2.toString()); in.close(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
4、依赖注入(Spring DI)
依赖注入从JVM的角度来看的话,应该不算是一种创建对象的方式。关于依赖注入的概念,请参考Spring官方网站。
Java单例的几种实现方式
根据单例模式的定义及描述,要实现单例模式,有几个问题是需要考虑的。单例如何创建以及由谁创建,如何保证实例的唯一性,如何提供全局的访问点。由于Java语言中的实例化有多种,所以Java单例必须能够保证在各种条件下维持instance的唯一性,能够做到线程安全、延迟加载并且能够很好的抵抗反射攻击、反序列化攻击等。本文的以下内容将分析和验证常见的几种单例的设计方法,并用代码来验证效果(网上理论讲的很多,但是大部分都缺少实际的代码验证)。
实现单例,必须是私有的构造器,并导出公有的静态实例,根据静态实例的初始化方式,可以分为饿汉型和懒汉型,根据实例的暴露方式可以分为直接暴露和工厂方法间接暴露。饿汉型在类加载的时候就完成实例化,懒汉型就是在实际使用的时候才进行实例化。因此饿汉型的单例是能够保证线程安全的(JVM来保证ClassLoader只加载一次,不考虑多个ClassLoader的情况),懒汉型的线程安全却需要额外的同步来控制;饿汉型不能实现懒汉式的延迟加载。
1、直接暴露-饿汉型(1)
这种写法非常简单、粗暴,而且也能较好的满足需要,呵呵,有时候简单粗暴也是最能快速解决问题的。这种方式线程安全,但是缺乏灵活性,不容易扩展。
import java.io.Serializable; public class DirectHungrySingleton implements Serializable{ private static final long serialVersionUID = 6069877357741265707L; //实例对象 public static final DirectHungrySingleton singleton = new DirectHungrySingleton(); //私有的构造方法 private DirectHungrySingleton(){ } }2、工厂方法-饿汉型
相关推荐
自己总结的6中单例模式的写法,也有测试类,可以试验下,自己稍微修改一下后,验证安全性,纯粹为学习,建议可提
单例的多种写法效率和方法同步比较说明,常用懒汉和饥汉模式等
java单例模式开发的7中写法,网上搜索的,可以看看
此文档为Tom老师的公开课的单例的7种写法的一个文档,充分分析单例模式,值得对设计模式有研究的童鞋下下来好好看看
单例模式的七种写法: 1.第一种(懒汉,线程不安全) 2.第二种(懒汉,线程安全) 3.第三种(饿汉) .....
android资料 单例模式的八种写法比较 单例模式是一种常用的软件设计模式,其定义是单例对象的类只能允许一个实例存在。
java for循环的几种写法
JavaSE单例模式各种写法单例模式
深入分析java单例模式什么是单例模式单例模式的常见写法一、饿汉式单例优点缺点示例二、懒汉式单例示例1(普通写法)示例2(synchronized写法)示例3(DCL写法)示例4(内部类写法)三、注册式单例示例1(容器式)示例2(枚举式...
在java中,单例有很多种写法,面试时,手写代码环节,除了写算法题,有时候也会让手写单例模式,这里记录一下单例的几种写法和优缺点。需要的朋友可以参考下
【Java面试题】单例的写法
主要介绍了IOS 中两种单例模式的写法实例详解的相关资料,需要的朋友可以参考下
unity中在场景切换时需要经常数据分享并处理,在此分享给大家利用C#模式和Unity模式分别实现的单例共享数据
NULL 博文链接:https://kaka100.iteye.com/blog/1060519
第一种(懒汉,线程不安全): 1 public class Singleton { 2 private static Singleton instance; 3 private Singleton (){} 4 public static Singleton getInstance() { 5 if ...
主要介绍了PHP里的单例类写法实例,本文直接给出代码实例,需要的朋友可以参考下
2、单例类必须自己创建自己的唯一实例 3、单例类必须给所有其他对象提供这一实例 2、资源加载和性能:饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会
关于Java中素数的概念,及Java代码的写法,写了几种方法