2008年4月5日星期六

你可能不知道的:突破Java单例模式

这个东西是以前写代码注入时发现的。这两天实在无聊,所以写出来,仅供参考。MS没什么使用价值。。。。权当娱乐
我们知道,在Java中有一种单例模式,是在一个虚拟机里,某个对象只有一个实例。但是,真的只能有一个实例么?
考虑下面代码:


import java.io.InputStream;
public interface IOnlyOne{
void printOne();
}
public class OnlyOne implements IOnlyOne{
private static final OnlyOne myOne = new OnlyOne();
public void printOne(){
System.out.println(myOne);
}
public static void main(String[] args) throws Exception{
myOne.printOne();
OnlyOneLoader loader = new OnlyOneLoader();
IOnlyOne o = loader.createNewOne();
o.printOne();
}
}
public class OnlyOneLoader extends ClassLoader{
public IOnlyOne createNewOne() throws Exception{
InputStream is = getClass().getResourceAsStream("OnlyOne.class");
byte[] b = new byte[is.available()];
is.read(b);
Class clz = defineClass(null,b, 0, b.length);
Object o = clz.newInstance();
return (IOnlyOne) o;
}
}

我们看到createNewOne()首先定义了一个类OnlyOne,然后实例化了一个对象。那么根据代码来看,这个对象的printOne方法又会访问那个myOne的静态成员。看起来两次打印的结果应该是相同的,但是事实上,结果并不相同。
那么,这是为什么呢?是不是定义类过程中又对那个myOne对象重新赋值了呢?我们可以在o.printOne();后面再加上一句myOne.printOne();
可以看到,打印的和第一次的结果是一样的。这就说明了并不是重新赋值,而是有两个myOne的变量存在。
事实上,Java中,一个类是可以存在多个定义的,这些类的类名可以相同,但是确可以有各自的成员存储区,他们的代码也可以不同。在同一个 ClassLoader中,不允许出现重名类,但是Java却允许多个不同的ClassLoader同时存在。需要注意的是:即使类名和代码都相同,但是不同ClassLoader说产生的类是不兼容的,这就是为什么上述代码中要引入一个接口的原因了。你可以把那个接口去掉,看看会发生什么有趣的现象~~
那么,这个和单例模式有什么关系呢?因为单例的对象实例是存储在静态成员变量中的,所以在不同的ClassLoader下,这个变量的值可能是不同的。

0 人次吐槽:

发表评论