单例(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中,有多种实现单例模式的方法,每种都有其特点和适用场景。接下来,我们将深入探讨这些实现方式。 首先,我们来看**懒汉式(Lazy Initialization)**。这种实现方式是在类被首次请求时才创建单例对象,延迟...
本文介绍了一种使用Java单例模式实现简单日志记录器的方法。通过定义静态变量、私有构造函数以及线程安全的实例获取方法,实现了日志记录器的单例模式。此外,还提供了写入日志的方法,便于实际应用中的日志管理。...
### JAVA单例模式的几种实现方法 #### 一、饿汉式单例模式 饿汉式单例模式是在类初始化时就已经完成了实例化的操作。这种模式下,类一旦被加载,就会创建一个单例对象。 ##### 代码示例: ```java class ...
### Java 单例模式详解 #### 一、什么是单例模式? 单例模式是一种常用的软件设计模式,在这种模式中,一个类只能拥有一个实例,并且该类必须自行创建并提供这个实例。通常,单例模式用于确保某个类在整个应用程序...
本篇文章将深入探讨如何在Java中实现单例模式,以及如何创建一个工具类来封装单例的创建。 首先,让我们了解单例模式的几种常见实现方式: 1. 饿汉式(静态常量): 这是最简单的单例实现,它在类加载时就完成了...
以下是Java实现的六种单例模式的详细解释: 1. 懒汉式(Lazy Initialization): 这种方式延迟了单例对象的初始化,直到第一次被请求时。例如,`SingleInstance1.java`可能就实现了这种方式。代码通常包含一个私有...
java单例模式的多种实现方法及优劣区分,以及如何摧毁单例模式。能够让大家认识到单例的多种多样
Java单例模式及实现 Java单例模式是一种常见的设计模式,确保某一个类只有一个实例,而且向这个系统提供这个实例。单例模式可以分为三种:懒汉式单例、饿汉式单例、登记式单例。 单例模式的要点 1. 某个类只能有...
首先,我们需要了解Java中的单例模式实现方式。常见的有懒汉式、饿汉式、双重检查锁定(DCL)以及静态内部类四种。其中,DCL和静态内部类是最推荐的,因为它们既实现了线程安全,又保证了单例的唯一性,并且延迟加载...
"Java单例模式实现静态内部类方法示例" Java单例模式是软件设计模式中最基本和最常见的一种设计模式,也是最容易理解的一种设计模式。它的主要思想是确保某个类只有一个实例,并且提供一个全局访问点来访问该实例。...
总结起来,Java单例模式有多种实现方式,每种都有其适用场景。饿汉单例适用于对性能敏感且对延迟初始化无要求的情况;懒汉单例则适合那些需要延迟初始化且对线程安全有要求的场景;而考虑到序列化和反序列化的影响,...
### Java单例模式应用研究 #### 一、单例模式概述 单例模式(Singleton Pattern)作为一种最基本的创建型设计模式,其主要目的是控制一个类的实例化过程,确保在整个应用程序中仅存在一个实例,并且该实例能够被全局...
在Java中,常见的单例模式实现方式有以下几种: 1. 饿汉式(静态常量): 这种实现方式在类加载时就完成了初始化,所以是线程安全的。代码如下: ```java public class Singleton { private static final ...
Java中的单例模式主要分为三种实现方式:懒汉式单例、饿汉式单例和登记式单例。 1. 懒汉式单例(Lazy Initialization) 懒汉式单例的特点是在类被加载时不创建实例,而是在首次调用`getInstance()`方法时才进行实例...
以下是几种常见的单例模式实现方式: 1. **饿汉式(静态常量)**: 这是最简单的实现方式,它在类加载时就完成了初始化,因此是线程安全的。 ```java public class Singleton { private static final Singleton...
以上就是Java中实现单例模式的常见方法,每种方式都有其适用场景和优缺点。在实际开发中,我们需要根据项目需求选择合适的方式实现单例。同时,理解单例模式背后的原理和应用场景,有助于提升代码的设计质量。
首先,让我们了解几种常见的Java单例模式实现方法: 1. 饿汉式(静态常量): 这种方式在类加载时就完成了初始化,所以是线程安全的。 ```java public class Singleton { private static final Singleton ...