文档章节

类加载器与单例

乒乓狂魔
 乒乓狂魔
发布于 2015/02/07 10:38
字数 802
阅读 257
收藏 2
当使用不同的类加载器时,也会使单例失效,如下:
单例为:

public final class Singleton{
	
	private static final Singleton instance=new Singleton();
	
	private Singleton(){
                System.out.println("执行构造函数");
		System.out.println("类加载器="+this.getClass().getClassLoader());
	}
	
	public static Singleton getInstance(){
		return instance;
	}

}

自定义的类加载器为:
public class MyClassLoader extends ClassLoader{
	
	private String name;
	private String classPath;
	
	public MyClassLoader(String name){
        super(null);
        this.name = name;
    }
	
	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		byte[] b=getClassBytes(name);
		return this.defineClass(name, b,0,b.length);
	}

	private byte[] getClassBytes(String name) {
		String classFullPath=classPath+"/"+name.replace(".","/")+".class";
		byte[] data=null;
		try {
			FileInputStream fileInputStream=new FileInputStream(classFullPath);
			ByteArrayOutputStream out=new ByteArrayOutputStream();
			IOUtils.copy(fileInputStream,out);
			data=out.toByteArray();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return data;
	}
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getClassPath() {
		return classPath;
	}

	public void setClassPath(String classPath) {
		this.classPath = classPath;
	}

}

测试案例如下:
public static void testClassLoader() throws Exception{
		Singleton singleton=Singleton.getInstance();
		
		MyClassLoader myClassLoader=new MyClassLoader("myClassLoader");
		myClassLoader.setClassPath("D:/important");
		Class singletonClass=myClassLoader.findClass("com.lg.design.singleton.hungry.Singleton");
		System.out.println("singletonClass.getClassLoader() : "+singletonClass.getClassLoader());
		
		System.out.println("Singleton.class==singletonClass : "+(Singleton.class==singletonClass));
		System.out.println("Singleton.class.equals(singletonClass) : "+(Singleton.class.equals(singletonClass)));
		
		Constructor constructor1=Singleton.class.getDeclaredConstructor();
		Constructor constructor2=Singleton.class.getDeclaredConstructor();
		Constructor constructor3=singletonClass.getDeclaredConstructor();
		System.out.println("constructor1==constructor2 : "+(constructor1==constructor2));
		System.out.println("constructor1.equals(constructor2) : "+constructor1.equals(constructor2));
		System.out.println("constructor1==constructor : "+(constructor1==constructor3));
		System.out.println("constructor1.equals(constructor3) : "+constructor1.equals(constructor3));
		
		constructor1.setAccessible(true);
		Object singleton1=constructor1.newInstance();
		constructor3.setAccessible(true);
		Object singleton3=constructor3.newInstance();
		
		System.out.println("singleton : "+singleton);
		System.out.println("singleton1 : "+singleton1);
		System.out.println("singleton3 : "+singleton3);
		System.out.println("singleton1==singleton3 : "+(singleton1==singleton3));
	}

输出结果为:
执行构造函数
类加载器=sun.misc.Launcher$AppClassLoader@417470d0
singletonClass.getClassLoader() : com.lg.design.singleton.hungry.MyClassLoader@470d1f30
Singleton.class==singletonClass : false
Singleton.class.equals(singletonClass) : false
constructor1==constructor2 : false
constructor1.equals(constructor2) : true
constructor1==constructor : false
constructor1.equals(constructor3) : false
执行构造函数
类加载器=sun.misc.Launcher$AppClassLoader@417470d0
singleton : com.lg.design.singleton.hungry.Singleton@77e3cabd
singleton1 : com.lg.design.singleton.hungry.Singleton@c137bc9
singleton3 : com.lg.design.singleton.hungry.Singleton@5323cf50
singleton1==singleton3 : false

咱们慢慢来看这些信息。
1 Singleton.class与singletonClass
前者是系统类加载器加载器的,后者是我们自定义的类加载器加载的,虽然他们的字节码相同,但由不同的类加载器加载后就是不同的类了,所以两者的==和eaquals都为false。
2 constructor1、constructor2、constructor3
constructor1、constructor2都是通过调用Singleton.class.getDeclaredConstructor()得来的,但是两者并不是同一个对象,他们的==为false,equals为true。看getDeclaredConstructor源码就可以理解:

private Constructor<T> getConstructor0(Class<?>[] parameterTypes,
                                        int which) throws NoSuchMethodException
    {
        Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
        for (Constructor<T> constructor : constructors) {
            if (arrayContentsEq(parameterTypes,
                                constructor.getParameterTypes())) {
                //这里在获取构造器的时候就是用的复制
                return getReflectionFactory().copyConstructor(constructor);
            }
        }
        throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));
    }

再看构造器的eequals方法
public boolean equals(Object obj) {
        if (obj != null && obj instanceof Constructor) {
            Constructor<?> other = (Constructor<?>)obj;
            if (getDeclaringClass() == other.getDeclaringClass()) {
                /* Avoid unnecessary cloning */
                Class<?>[] params1 = parameterTypes;
                Class<?>[] params2 = other.parameterTypes;
                if (params1.length == params2.length) {
                    for (int i = 0; i < params1.length; i++) {
                        if (params1[i] != params2[i])
                            return false;
                    }
                    return true;
                }
            }
        }
        return false;
    }

先通过比较是否是同一个类的构造器,然后再比较他们的参数是否一致,所以constructor1和constructor2的equals方法为true。对于constructor3和constructor1、constructor2,他们所属的类就是不一样的,即getDeclaringClass() == other.getDeclaringClass()为false。

3 singleton1和singleton3
singleton1是由constructor1构造器通过反射生成的对象,constructor3是通过constructor3构造器通过反射生成的对象,这些对象肯定都不是同一个对象。我有个疑问就是:通过constructor1.newInstance()会去执行Singleton的无参构造函数,打印出

执行构造函数
类加载器=sun.misc.Launcher$AppClassLoader@417470d0

然而执行constructor3.newInstance()却并没有打印出无参构造函数中的信息,这背后的原理希望你们能帮我解答。
有关类加载器的内容,请见后续文章

若想转载请注明出处:   http://lgbolgger.iteye.com/blog/2161094
作者:iteye的乒乓狂魔

© 著作权归作者所有

共有 人打赏支持
上一篇: 单例模式大汇总
下一篇: 序列化与单例
乒乓狂魔
粉丝 1011
博文 105
码字总数 271356
作品 0
长宁
程序员
私信 提问
加载中

评论(4)

零度冰川

引用来自“乒乓狂魔”的评论

对于第一个问题,这两个对象本应该就是不一样的,一旦能用反射,就没有单例可言了,枚举但是可以阻止反射。对于第二个问题,就是在加载类的时候就会去执行static方法,执行的时候发现是new 本身,就去执行构造函数了。类加载完成之后,紧接着执行constructor3.newinstance方法,又会执行一次构造方法,所以会执行两次
哦,多谢回答
乒乓狂魔
乒乓狂魔

引用来自“零度冰川”的评论

博主,有两个问题:
1、我分别用上面说的constructor1和constructor2执行了newInstance(),返回obj1和obj2,然后比对了两个的结果发现new出来的实例不相等,我查看了了一下两个对象的hashcode发现不一样,按说这两个都是同一个系统类加载器加载出来的单例对象,应该equal才对,题主可以帮忙解答一下吗?
2、我使用constructor3.newInstance(),打印出两次无参构造函数里面的信息,我单步了一下,发现constructor3.newInstance()的时候先执行的是无参构造函数,然后再执行的private static final Singleton instance=new Singleton();这一点我有点理解不了,博主知道为什么吗?

见上面回复
乒乓狂魔
乒乓狂魔
对于第一个问题,这两个对象本应该就是不一样的,一旦能用反射,就没有单例可言了,枚举但是可以阻止反射。对于第二个问题,就是在加载类的时候就会去执行static方法,执行的时候发现是new 本身,就去执行构造函数了。类加载完成之后,紧接着执行constructor3.newinstance方法,又会执行一次构造方法,所以会执行两次
零度冰川
博主,有两个问题:
1、我分别用上面说的constructor1和constructor2执行了newInstance(),返回obj1和obj2,然后比对了两个的结果发现new出来的实例不相等,我查看了了一下两个对象的hashcode发现不一样,按说这两个都是同一个系统类加载器加载出来的单例对象,应该equal才对,题主可以帮忙解答一下吗?
2、我使用constructor3.newInstance(),打印出两次无参构造函数里面的信息,我单步了一下,发现constructor3.newInstance()的时候先执行的是无参构造函数,然后再执行的private static final Singleton instance=new Singleton();这一点我有点理解不了,博主知道为什么吗?
单例设计模式 (2)

1.静态内部类实现 在上一版的时候用的是懒汉和饿汉来做单例模式,如果我们采用静态内部类的话,就可以通过classloader来懒加载单例 用静态内部类实现单例模式: 这里有几个需要注意的点: 1....

蠢廿
2017/12/04
0
0
单例类在多个classLoader里怎么保持唯一性的问题

在看单例的帖子时看到一个问题: 如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些servlet容器对每个servlet使用完全不同的类装载器,这样的话如...

jakic
2013/03/21
1K
2
设计模式-单例模式

单例模式 单例模式特点 保证一个类只有一个实例,并且提供一个访问该实例的全局访问点 windows中的任务管理器 操作系统的文件系统,一个操作系统只能有一个文件系统 servlet编程中,每个ser...

wjk_snail
2016/03/16
36
0
你敢说自己了解单例模式?

一、背景   最近在学习设计模式,在看到单例模式的时候,我一开始以为直接很了解单例模式了,实现起来也很简单,但是实际上单例模式有着好几个变种,并且多线程中涉及到线程安全问题,那么...

hafiz.zhang
2017/09/27
0
0
设计模式(十一)[创建模式] 单例模式(Singleton)

单例模式的要点:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。大多数的软件都有一个(甚至多个)属性文件存放系统配置。这样的系统应当由...

1527
08/13
0
0

没有更多内容

加载失败,请刷新页面

加载更多

jquery通过id显示隐藏

var $div3 = $('#div3'); 显示 $div3.show(); 隐藏 $div3.hide();

yan_liu
今天
3
0
《乱世佳人》读书笔记及相关感悟3900字

《乱世佳人》读书笔记及相关感悟3900字: 之前一直听「荔枝」,后来不知怎的转向了「喜马拉雅」,一听就是三年。上班的时候听房产,买房了以后听装修,兴之所至时听旅行,分手后听亲密关系,...

原创小博客
今天
3
0
大数据教程(9.6)map端join实现

上一篇文章讲了mapreduce配合实现join,本节博主将讲述在map端的join实现; 一、需求 实现两个“表”的join操作,其中一个表数据量小,一个表很大,这种场景在实际中非常常见,比如“订单日志...

em_aaron
今天
3
0
cookie与session详解

session与cookie是什么? session与cookie属于一种会话控制技术.常用在身份识别,登录验证,数据传输等.举个例子,就像我们去超市买东西结账的时候,我们要拿出我们的会员卡才会获取优惠.这时...

士兵7
今天
3
0
十万个为什么之为什么大家都说dubbo

Dubbo是什么? 使用背景 dubbo为什么这么流行, 为什么大家都这么喜欢用dubbo; 通过了解分布式开发了解到, 为适应访问量暴增,业务拆分后, 子应用部署在多台服务器上,而多台服务器通过可以通过d...

尾生
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部