单例(Singleton)是指只实例化一次的类。
一、如何实现单例
单例类常用的实现方式有以下几种
1、饿汉式:类载入时创建单例实例
1.1、公开的静态常量单例对象
/**
* *
* <p>
* *
* <h6>当类StaticFinalSingleton类被实例化时,有且仅有一个StaticFinalSingleton实例存在 *
* (除非客户端通过类反射机制调用该私有的构造方法,否则外部无法改变单例)</h6> * * @Copyright 2011 *
* </p>
*/
public class StaticFinalSingleton {
/** * 公有静态常量单例对象 */
public static final StaticFinalSingleton INSTANCE = new StaticFinalSingleton();
/** * 私有构造方法 */
private StaticFinalSingleton() {
}
}
1.2、静态工厂方法获取私有静态常量单例对象
/**
* *
* <p>
* *
* <h6>当类StaticFinalSingleton类被实例化时,有且仅有一个StaticFinalSingleton实例存在 *
* (除非客户端通过类反射机制调用该私有的构造方法,否则外部无法改变单例)</h6> * * @Copyright 2011 *
* </p>
*/
public class StaticFinalSingleton {
/** * 私有静态常量单例对象 */
private static final StaticFinalSingleton INSTANCE = new StaticFinalSingleton();
/** * 私有构造方法 */
private StaticFinalSingleton() {
}
/** * 静态工厂方法获取StaticFinalSingleton型的私有静态常量单例对象 */
public static StaticFinalSingleton getInstanceFactory() {
return INSTANCE;
}
}
2、懒汉式:延迟加载创建单例实例
2.1、基于静态同步锁的lazy initialization单例类
此种方法不常用,因为在实际中会大大影响并发度,性能较差
/**
* <p>
* <h6>基于静态同步锁的lazy initialization单例类:<br>
* 此种方法不常用,因为在实际中会大大影响并发度,并且可能不够安全</h6>
*
* @Copyright 2011
* </p>
*/
public class SynLockSingleton {
/**
* 私有静态常量单例对象
*/
private static SynLockSingleton INSTANCE = null;
/**
* 私有构造方法
*/
private SynLockSingleton() {
}
/**
* 返回单例实例
*/
public static synchronized SynLockSingleton getInstanceFactory() {
if (INSTANCE == null) {
INSTANCE = new SynLockSingleton();
}
return INSTANCE;
}
}
2.2、基于双检查锁机制的lazy initialization单例类
双检查锁只有在 jdk 1.5 及以上版本才能达到单例的效果,因此不常用
/**
* <p>
* <h6>基于双检查锁机制的lazy initialization单例类:<br>
* 双检查锁只有在 jdk 1.5 及以上版本才能达到单例的效果,因此不常用</h6>
*
* @Copyright 2011
* </p>
*/
public class DCLSingleton {
private static DCLSingleton INSTANCE = null;
/**
* 私有构造方法
*/
private DCLSingleton() {
}
/**
* 返回单例实例
*/
public static DCLSingleton getInstance() {
if (INSTANCE == null) {
synchronized (DCLSingleton.class) {
if (INSTANCE == null) {
INSTANCE = new DCLSingleton();
}
}
}
return INSTANCE;
}
}
2.3、基于静态内部类的lazy initialization单例类
/**
* <p>
* <h6>基于静态内部类的lazy initialization单例类<br>
* </h6>
*
* @Copyright 2011
* </p>
*/
public class StaticInnerSingleton {
/**
* 私有构造方法
*/
private StaticInnerSingleton() {
}
private static final class InnerBuildSingleton {
private static final StaticInnerSingleton INSTANCE = new StaticInnerSingleton();
}
/**
* 返回单例实例
*/
public static StaticInnerSingleton getInstanceFactory() {
return InnerBuildSingleton.INSTANCE;
}
}
3、基于枚举实现单例
使用枚举实现单例:如果枚举中只有一个元素,那么就可以使用单例设计模式,java1.5以上提供的功能。
此种方式应该是最有效和最安全的单例实现方式,由JVM控制单例的初始化和管理,并且避免了通过反射再次生成单例的可能
/**
* <p>
* <h6>使用枚举实现单例:如果枚举中只有一个元素,那么就可以使用单例设计模式</h6>
*
* @Copyright 2011
* </p>
*/
public enum EnumSingleton {
/**
* 枚举类型 INSTANCE 就是一个单例,引用方式为 EnumSingleton.INSTANCE
*/
INSTANCE;
// 后面是单例中的属性及方法,如
void doSomething() {
}
private EnumSingleton() {
}
public static void main(String[] args) {
// 引用单例并使用其方法
EnumSingleton.INSTANCE.doSomething();
}
}
二、Sington类的其他注意事项
1、通过反射生成Sington类的额外实例
在不是通过枚举实现单例时,均面临一个问题,如果通过java的反射机制,是可以生成此单例的额外实例的
1、1举例
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class StaticFinalSingleton {
/** * 公有静态常量单例对象 */
public static final StaticFinalSingleton INSTANCE = new StaticFinalSingleton();
/** * 私有构造方法 */
private StaticFinalSingleton() {
}
/** * 返回据类名通过反射得到的实例 * * @param String * className * @return Object */
@SuppressWarnings("unchecked")
public static Object getReflectInstance(String className)
throws ClassNotFoundException, IllegalArgumentException,
InstantiationException, IllegalAccessException,
InvocationTargetException {
Class c1 = Class.forName(className);
Constructor[] cons = c1.getDeclaredConstructors();
Constructor cc1 = cons[0];
cc1.setAccessible(true);
return cc1.newInstance(null);
}
public static void main(String[] args) {
try {
// 首先获取单例实例
StaticFinalSingleton s1 = StaticFinalSingleton.INSTANCE;
// 通过反射,生成此单例类新的实例
StaticFinalSingleton s2 = (StaticFinalSingleton) getReflectInstance("StaticFinalSingleton");
System.out.println("object instance --s1=" + s1 + ",s2=" + s2);
System.out.println("object instance (s1==s2)=" + (s1 == s2));
} catch (Exception e) {
e.printStackTrace();
}
}
}
输出如下
object instance --s1=StaticFinalSingleton@de6ced,s2=StaticFinalSingleton@c17164
object instance (s1==s2)=false
1.2 解决方法
可能的一个解决方法,在私有的构造函数中做检查,判断是否已经实例化了单例类,如果重复实例化,则抛RuntimeException异常
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class StaticFinalSingleton {
private static boolean isFirstFlag = false;
/** * 公有静态常量单例对象 */
public static final StaticFinalSingleton INSTANCE = new StaticFinalSingleton();
/** * 私有构造方法 */
private StaticFinalSingleton() {
if (isFirstFlag) {
throw new java.lang.RuntimeException(
"can not initialization class=StaticFinalSingleton");
} else {
isFirstFlag = true; // do initialization work
}
}
/** * 返回据类名通过反射得到的实例 * * @param String * className * @return Object */
@SuppressWarnings("unchecked")
public static Object getReflectInstance(String className)
throws ClassNotFoundException, IllegalArgumentException,
InstantiationException, IllegalAccessException,
InvocationTargetException {
Class c1 = Class.forName(className);
Constructor[] cons = c1.getDeclaredConstructors();
Constructor cc1 = cons[0];
cc1.setAccessible(true);
return cc1.newInstance(null);
}
public static void main(String[] args) {
try {
// 首先获取单例实例
StaticFinalSingleton s1 = StaticFinalSingleton.INSTANCE;
// 通过反射,生成此单例类新的实例
StaticFinalSingleton s2 = (StaticFinalSingleton) getReflectInstance("StaticFinalSingleton");
System.out.println("object instance --s1=" + s1 + ",s2=" + s2);
System.out.println("object instance (s1==s2)=" + (s1 == s2));
} catch (Exception e) {
e.printStackTrace();
}
}
}
此时,通过反射无法实现创建额外的单例
输出如下所示:
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at StaticFinalSingleton.getReflectInstance(StaticFinalSingleton.java:47)
at StaticFinalSingleton.main(StaticFinalSingleton.java:55)
Caused by: java.lang.RuntimeException: can not initialization class=StaticFinalSingleton
at StaticFinalSingleton.<init>(StaticFinalSingleton.java:23)
... 6 more
2、多个类加载实现单例类的加载
在许多情况下,使用多个类载入器是很普遍的--包括servlet容器--所以不管你在实现你的单例类时是多么 小心你都最终可以得到多个单例类的实例。如果你想要确保你的单例类只被同一个的类载入器装入,就必须自己指定这个类载入器
public class StaticFinalSingleton {
public static final StaticFinalSingleton INSTANCE = new StaticFinalSingleton();
/**
* 私有构造方法
*/
private StaticFinalSingleton() {
}
/**
* 重写getClass方法以应对多个类载入器的情况
*
* @param String
* className
* @return Class
*/
@SuppressWarnings("unused")
private static Class getClass(String classname)
throws ClassNotFoundException {
ClassLoader classLoader = Thread.currentThread()
.getContextClassLoader();
if (classLoader == null)
classLoader = StaticFinalSingleton.class.getClassLoader();
return (classLoader.loadClass(classname));
}
}
方法会尝试把当前的线程与那个类载入器相关联;如果classloader为null,这个方法会使用与装入单例类基类的那个类载入器
3、单例类的序列化
为了使Singleton类变成可序列化的(serializable),仅仅实现Serializable接口是不够的。为了维护 Singleton的单例性,你必须给Singleton类提供一个readResolve方法,否则的话,一个序列化的实例,每次反序列化的时候都会产生一个新的实例。Singleton 也不会例外。
3.1举例
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class StaticFinalSingleton implements java.io.Serializable {
private static final long serialVersionUID = 2474672098172796959L;
public static final StaticFinalSingleton INSTANCE = new StaticFinalSingleton();
/** * 私有构造方法 */
private StaticFinalSingleton() {
}
public static void main(String[] args) {
ObjectOutputStream objectOutputStream = null;
ObjectInputStream objectInputStream = null;
try {
// 序列化
objectOutputStream = new ObjectOutputStream(new FileOutputStream(
"C:\\StaticFinalSingleton.obj"));
StaticFinalSingleton singleton1 = StaticFinalSingleton.INSTANCE;
objectOutputStream.writeObject(singleton1);
objectOutputStream.close();
// 反序列化
objectInputStream = new ObjectInputStream(new FileInputStream(
"C:\\StaticFinalSingleton.obj"));
StaticFinalSingleton singleton2 = (StaticFinalSingleton) objectInputStream
.readObject();
objectInputStream.close();
// 比较是否原来的实例
System.out.println("object instance (singleton1==singleton2)="
+ (singleton1 == singleton2));
} catch (Exception e) {
} finally {
if (objectOutputStream != null) {
try {
objectOutputStream.close();
} catch (IOException e) {
}
}
if (objectInputStream != null) {
try {
objectInputStream.close();
} catch (IOException e) {
}
}
}
}
}
输出如下
object instance (singleton1==singleton2)=false
3.2解决方法
Serializable接口确实有这样两个特殊的方法描述:
Object writeReplace() throws ObjectStreamException;
此 writeReplace 方法将由序列化调用,前提是如果此方法存在,而且它可以通过被序列化对象的类中定义的一 个方法访问。因此,该方法可以拥有私有 (private)、受保护的 (protected) 和包私有 (package-private) 访 问。子类对此方法的访问遵循 java 访问规则。
在从流中读取类的一个实例时需要指定替代的类应使用的准确签名来实现此特殊方法:
Object readResolve() throws ObjectStreamException;
此 readResolve 方法遵循与 writeReplace 相同的调用规则和访问规则。
上述两个方法的只要出现,就会履盖以下两个方法(这两个方法本质的意义就是用来替换序列与反序列的对 象),虽然会执行它们,但最后得到的结果却是writeReplace、readResolve两个方法写入或读出的对象:
private void writeObject(java.io.ObjectOutputStream out) throws IOException
private void readObject(java.io.ObjectInputStream in)throws IOException, ClassNotFoundException;
另外,writeObject与readObject需成对实现,而writeReplace与readResolve则不需要成对出现,一般单独使 用。如果同时出现这四个方法,最后写入与读出的结果以writeReplace和readResolve方法的结果为准。
因此我们只要为Singleton类增加readResolve()方法:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class StaticFinalSingleton implements java.io.Serializable {
private static final long serialVersionUID = 2474672098172796959L;
public static final StaticFinalSingleton INSTANCE = new StaticFinalSingleton();
/** * 私有构造方法 */
private StaticFinalSingleton() {
}
/**
* 反序列化时实际上用INSTANCE代替读取的对象,达到唯一一个实例
*
* @param String
* className
* @return Class
*/
private Object readResolve() {
return INSTANCE;
}
public static void main(String[] args) {
ObjectOutputStream objectOutputStream = null;
ObjectInputStream objectInputStream = null;
try {
// 序列化
objectOutputStream = new ObjectOutputStream(new FileOutputStream(
"C:\\StaticFinalSingleton.obj"));
StaticFinalSingleton singleton1 = StaticFinalSingleton.INSTANCE;
objectOutputStream.writeObject(singleton1);
objectOutputStream.close();
// 反序列化
objectInputStream = new ObjectInputStream(new FileInputStream(
"C:\\StaticFinalSingleton.obj"));
StaticFinalSingleton singleton2 = (StaticFinalSingleton) objectInputStream
.readObject();
objectInputStream.close();
// 比较是否原来的实例
System.out.println("object instance (singleton1==singleton2)="
+ (singleton1 == singleton2));
} catch (Exception e) {
} finally {
if (objectOutputStream != null) {
try {
objectOutputStream.close();
} catch (IOException e) {
}
}
if (objectInputStream != null) {
try {
objectInputStream.close();
} catch (IOException e) {
}
}
}
}
}
输出如下所示:
object instance (singleton1==singleton2)=true
分享到:
相关推荐
一个简单的java工程,包含注释,一目了然,其中包含了单例模式的所有实现方式,懒汉式,饿汉式,双重校验,枚举,静态内部类等方式实现单例。
详细讲解了Java单例模式的几种实现方式,并有详细的示例配合讲解.
作为对象的创建模式, 单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。 PPT详细了单例模式的实现和使用场景
java单例模式的实现方式
java单例模式的多种实现方法及优劣区分,以及如何摧毁单例模式。能够让大家认识到单例的多种多样
java单例模式及实现
通过Java语言,主要实现了六种单例的生成方法,包括懒汉式、饿汉式、双重校验锁、枚举、静态内部类,可以根据实际情况选择使用
这段代码实现了一个简单的日志记录器Logger,其中使用了Java的单例模式。在类的构造方法中,首先定义...这个简单的示例代码展示了如何使用Java单例模式来实现一个日志记录器的简单功能,方便了对日志数据的管理和记录。
java单例模式连接数据库源码.可以直接引用只需加入相应的mysql 或 oracle的驱动,修改源码的连接地址用户名及密码。
目录 单例模式的概念 单例模式的要点 单例模式类图 单例模式归类 单例模式的应用场景 单例模式解决的问题 单例模式的实现方式 单例模式实现方式对比 单例模式的概念 单例模式,顾名思义就是只有一个实例,并且由它...
然后使用单例模式来实现该类的实例化,保证在整个应用程序中只有一个LoginSystem对象。在login()方法中对用户的用户名和密码进行校验,并返回校验结果。在main()方法中演示了如何使用单例模式来创建LoginSystem对象...
单例模式是最简单的一种设计模式,确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例, 本资源提供了用java语言实现简单的单例模式,供初学者参考
详解JAVA单例模式及多种实现
Java实现单例模式[汇编].pdf
主要介绍了Java单例模式实现的几种方式的相关资料,需要的朋友可以参考下
主要介绍了Java单例模式实现静态内部类方法示例,涉及构造函数私有化等相关内容,需要的朋友可以了解下。
单例模式的七种实现方法以及分析,可以作文大作业提交 1.前言 4 1.1 课题的研究背景 4 1.2 课题主要研究目标 4 2.相关技术简介 4 2.1Java简介 4 2.2IDEA简介 4 3. 单例模式的7种实现方式 5 3.1饿汉式(使用静态常量...
java单例模式详解 简单代码实现2中单例模式 有详细的文字说明。