Classloaders
在许多情况下,使用多个类载入器是很普通的--包括servlet容器--所以不管你在实现你的单例类时是多么小心你都最终可以得到多个单例类的实例。如果你想要确保你的单例类只被同一个的类载入器装入,那你就必须自己指定这个类载入器;例如:
- private static Class getClass(String classname)
- throws ClassNotFoundException {
- ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
-
- if(classLoader == null)
- classLoader = Singleton.class.getClassLoader();
-
- return (classLoader.loadClass(classname));
- }
- }
private static Class getClass(String classname)
throws ClassNotFoundException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if(classLoader == null)
classLoader = Singleton.class.getClassLoader();
return (classLoader.loadClass(classname));
}
}
这个方法会尝试把当前的线程与那个类载入器相关联;如果classloader为null,这个方法会使用与装入单例类基类的那个类载入器。这个方法可以用Class.forName()代替。
序列化
如果你序列化一个单例类,然后两次重构它,那么你就会得到那个单例类的两个实例,除非你实现readResolve()方法,像下面这样:
例12 一个可序列化的单例类
- import org.apache.log4j.Logger;
-
- public class Singleton implements java.io.Serializable {
- public static Singleton INSTANCE = new Singleton();
-
- protected Singleton() {
-
- }
- private Object readResolve() {
- return INSTANCE;
- }
- }
import org.apache.log4j.Logger;
public class Singleton implements java.io.Serializable {
public static Singleton INSTANCE = new Singleton();
protected Singleton() {
// Exists only to thwart instantiation.
}
private Object readResolve() {
return INSTANCE;
}
}
上面的单例类实现从readResolve()方法中返回一个唯一的实例;这样无论Singleton类何时被重构,它都只会返回那个相同的单例类实例。
例13测试了例12的单例类:
例13 测试一个可序列化的单例类
- import java.io.*;
- import org.apache.log4j.Logger;
- import junit.framework.Assert;
- import junit.framework.TestCase;
-
- public class SingletonTest extends TestCase {
- private Singleton sone = null, stwo = null;
- private static Logger logger = Logger.getRootLogger();
-
- public SingletonTest(String name) {
- super(name);
- }
- public void setUp() {
- sone = Singleton.INSTANCE;
- stwo = Singleton.INSTANCE;
- }
- public void testSerialize() {
- logger.info("testing singleton serialization...");
- <STRONG> writeSingleton();
- Singleton s1 = readSingleton();
- Singleton s2 = readSingleton();
- Assert.assertEquals(true, s1 == s2);</STRONG> }
- private void writeSingleton() {
- try {
- FileOutputStream fos = new FileOutputStream("serializedSingleton");
- ObjectOutputStream oos = new ObjectOutputStream(fos);
- Singleton s = Singleton.INSTANCE;
-
- oos.writeObject(Singleton.INSTANCE);
- oos.flush();
- }
- catch(NotSerializableException se) {
- logger.fatal("Not Serializable Exception: " + se.getMessage());
- }
- catch(IOException iox) {
- logger.fatal("IO Exception: " + iox.getMessage());
- }
- }
- private Singleton readSingleton() {
- Singleton s = null;
-
- try {
- FileInputStream fis = new FileInputStream("serializedSingleton");
- ObjectInputStream ois = new ObjectInputStream(fis);
- s = (Singleton)ois.readObject();
- }
- catch(ClassNotFoundException cnf) {
- logger.fatal("Class Not Found Exception: " + cnf.getMessage());
- }
- catch(NotSerializableException se) {
- logger.fatal("Not Serializable Exception: " + se.getMessage());
- }
- catch(IOException iox) {
- logger.fatal("IO Exception: " + iox.getMessage());
- }
- return s;
- }
- public void testUnique() {
- logger.info("testing singleton uniqueness...");
- Singleton another = new Singleton();
-
- logger.info("checking singletons for equality");
- Assert.assertEquals(true, sone == stwo);
- }
- }
import java.io.*;
import org.apache.log4j.Logger;
import junit.framework.Assert;
import junit.framework.TestCase;
public class SingletonTest extends TestCase {
private Singleton sone = null, stwo = null;
private static Logger logger = Logger.getRootLogger();
public SingletonTest(String name) {
super(name);
}
public void setUp() {
sone = Singleton.INSTANCE;
stwo = Singleton.INSTANCE;
}
public void testSerialize() {
logger.info("testing singleton serialization...");
writeSingleton();
Singleton s1 = readSingleton();
Singleton s2 = readSingleton();
Assert.assertEquals(true, s1 == s2); }
private void writeSingleton() {
try {
FileOutputStream fos = new FileOutputStream("serializedSingleton");
ObjectOutputStream oos = new ObjectOutputStream(fos);
Singleton s = Singleton.INSTANCE;
oos.writeObject(Singleton.INSTANCE);
oos.flush();
}
catch(NotSerializableException se) {
logger.fatal("Not Serializable Exception: " + se.getMessage());
}
catch(IOException iox) {
logger.fatal("IO Exception: " + iox.getMessage());
}
}
private Singleton readSingleton() {
Singleton s = null;
try {
FileInputStream fis = new FileInputStream("serializedSingleton");
ObjectInputStream ois = new ObjectInputStream(fis);
s = (Singleton)ois.readObject();
}
catch(ClassNotFoundException cnf) {
logger.fatal("Class Not Found Exception: " + cnf.getMessage());
}
catch(NotSerializableException se) {
logger.fatal("Not Serializable Exception: " + se.getMessage());
}
catch(IOException iox) {
logger.fatal("IO Exception: " + iox.getMessage());
}
return s;
}
public void testUnique() {
logger.info("testing singleton uniqueness...");
Singleton another = new Singleton();
logger.info("checking singletons for equality");
Assert.assertEquals(true, sone == stwo);
}
}
前面这个测试案例序列化例12中的单例类,并且两次重构它。然后这个测试案例检查看是否被重构的单例类实例是同一个对象。下面是测试案例的输出:
- Buildfile: build.xml
-
- init:
- [echo] Build 20030422 (22-04-2003 11:32)
-
- compile:
-
- run-test-text:
- [java] .INFO main: testing singleton serialization...
- [java] .INFO main: testing singleton uniqueness...
- [java] INFO main: checking singletons for equality
-
- [java] Time: 0.1
-
- [java] OK (2 tests)
Buildfile: build.xml
init:
[echo] Build 20030422 (22-04-2003 11:32)
compile:
run-test-text:
[java] .INFO main: testing singleton serialization...
[java] .INFO main: testing singleton uniqueness...
[java] INFO main: checking singletons for equality
[java] Time: 0.1
[java] OK (2 tests)
单例模式结束语
单例模式简单却容易让人迷惑,特别是对于Java的开发者来说。在这篇文章中,作者演示了Java开发者在顾及多线程、类载入器和序列化情况如何实现单例模式。作者也展示了你怎样才能实现一个单例类的注册表,以便能够在运行期指定单例类。
posted on 2008-05-05 22:12
bcterry 阅读(78)
评论(0) 编辑 收藏