类加载器与单例-阿里云开发者社区

开发者社区> 云计算> 正文

类加载器与单例

简介:
当使用不同的类加载器时,也会使单例失效,如下: 
单例为:
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
    }
 
}

自定义的类加载器为: 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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;
    }
 
}

测试案例如下: 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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));
    }

输出结果为: 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
执行构造函数
类加载器=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源码就可以理解:
 
1
2
3
4
5
6
7
8
9
10
11
12
13
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方法 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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的无参构造函数,打印出 

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

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

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
云计算
使用钉钉扫一扫加入圈子
+ 订阅

时时分享云计算技术内容,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。

其他文章