类加载器与单例
类加载器与单例
乒乓狂魔 发表于3年前
类加载器与单例
  • 发表于 3年前
  • 阅读 233
  • 收藏 2
  • 点赞 0
  • 评论 4

新睿云服务器60天免费使用,快来体验!>>>   

当使用不同的类加载器时,也会使单例失效,如下:
单例为:

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的乒乓狂魔
  • 打赏
  • 点赞
  • 收藏
  • 分享
共有 人打赏支持
粉丝 926
博文 104
码字总数 265075
评论 (4)
零度冰川
博主,有两个问题:
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();这一点我有点理解不了,博主知道为什么吗?

见上面回复
零度冰川

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

对于第一个问题,这两个对象本应该就是不一样的,一旦能用反射,就没有单例可言了,枚举但是可以阻止反射。对于第二个问题,就是在加载类的时候就会去执行static方法,执行的时候发现是new 本身,就去执行构造函数了。类加载完成之后,紧接着执行constructor3.newinstance方法,又会执行一次构造方法,所以会执行两次
哦,多谢回答
×
乒乓狂魔
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: