`

类加载器与单例

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

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的乒乓狂魔
分享到:
评论
2 楼 乒乓狂魔 2014-11-27  
edison87915 写道
   好细致

哈哈,类加载器还不详细,还需要慢慢研究,另写一篇文章
1 楼 edison87915 2014-11-27  
   好细致

相关推荐

    配置文件+类加载器(单例模式)连接数据库

    这里我们关注的是如何使用配置文件、类加载器以及单例模式来高效、稳定地管理数据库连接。Oracle数据库是一种广泛使用的商业关系型数据库系统,尤其适用于大型企业级应用。下面将详细阐述这些知识点。 首先,**配置...

    JVM类加载器说明文档

    3. 单例模式的实现:在多线程环境中,如果每个线程都有自己的类加载器,可能导致单例模式失效,因为每个加载器加载的类都是独立的实例。 2. 再分析类加载 类加载的过程包括三个主要步骤:加载(Loading)、验证...

    JVM实战-JVM类加载机制案例分析

    通过实践操作,掌握类的加载、连接与初始化过程,了解不同类型的类加载器及其工作原理,学会创建自定义的类加载器,并对类的卸载有所认识。实验将结合具体的Java程序实例,运用单例模式对静态变量和对象进行初始化,...

    单例模式,single

    2. **类加载器问题的解决**:为了解决不同类加载器加载同一个类产生的问题,可以在单例类中添加一个私有构造函数,并在构造函数中进行检查,确保每次加载的都是同一个实例。 3. **序列化问题的解决**:如果单例类...

    单例模式介绍

    这是最简单的单例实现方式,通过静态常量来存储唯一实例,保证在类加载时就完成初始化,因此线程安全。代码如下: ```java public class StaticSingleton { private static final StaticSingleton INSTANCE = new ...

    单例模式详解

    2. **多类加载器协同**:当应用程序中有多个类加载器时,如何确保不同类加载器下仍然保持单例的唯一性成为问题。此时需要考虑如何让单例对象能够在不同的类加载器之间共享。 3. **跨JVM环境**:在集群环境中或者...

    Java类加载内幕.pdf

    首先,类与数据在Java中的区别在于,类是执行的代码,包含了方法的定义和类的静态部分,而数据则是与类实例相关的状态,每个实例可以有不同的状态值。当创建一个类的实例时,就是将类的代码和特定的状态结合起来。在...

    ios播放单例类 singleton

    在`singleton-SoundManagerDemo`这个项目中,我们可以看到`SoundManager`的实现以及如何在其他视图控制器中使用这个单例。通常,你会在需要播放音频的地方调用`[SoundManager sharedManager]`来获取单例实例,然后...

    单例模式(Singleton)的6种实现

    另外,在测试时,单例类往往难以进行单元测试,因为它们没有公共的构造器,且通常会有一些全局状态,这会使得模拟这些对象变得复杂。 总的来说,单例模式虽然简单,但其设计和实现却蕴含着深刻的设计原则和考量,...

    单例省市级联

    单例模式恰好可以帮助我们实现这一目标,因为单例的数据管理类可以缓存已加载的省级和市级数据,减少不必要的网络请求。 在具体实现上,我们可以使用JavaScript或者Java等编程语言,结合前端框架(如React、Vue)...

    Java基于自定义类加载器实现热部署过程解析

    "Java基于自定义类加载器实现热部署过程解析" Java中基于自定义类加载器实现热部署是指在不重启应用的情况下,当类的定义即字节码文件修改后,能够替换该Class创建的对象。热部署是Java中的一个重要概念,它可以...

    Java设计模式之单例模式的七种写法

    Java 类加载器会先加载静态变量,然后加载对象实例化,最后加载成员变量和方法。 单例模式是一种常见的设计模式,它确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。懒汉式单例是一种常见的单例...

    使用Java单例模式实现一个简单的日志记录器.txt

    ### 使用Java单例模式实现一个简单的日志记录器 #### 一、单例模式简介 单例模式是一种常用的软件设计模式,在该模式中,一个类只能创建一个实例,并且提供了一个全局访问点来访问该实例。单例模式的主要优点包括...

    egret 单例场景管理类-一个小例子

    在 Egret 中,我们可以创建一个名为 `SceneManager` 的类,用作场景管理器。这个类将包含对所有场景的控制,包括加载、切换和卸载场景。为了实现单例,我们需要: 1. 将构造函数设为私有,防止外部直接实例化。 ```...

    C#单例模式详解 C#单例模式详解C#单例模式详解

    类型加载时初始化,与静态常量方式类似,但避免了不必要的内存分配。 ```csharp public class Singleton { private static readonly Singleton instance = new Singleton(); private Singleton() {} ...

    类加载机制与动态代理的测试题

    - 双亲委派模型是指在类加载过程中,如果一个类加载器收到了类加载请求,它首先不会自己尝试去加载这个类,而是把类加载请求委托给父类加载器完成。只有父类加载器无法完成加载时,才会尝试自己加载。 2. **Spring...

    单例模式(singleton)

    在描述中提到的随机数生成器就是一个典型的单例应用,它确保在整个应用程序运行期间,只有一个随机数生成器实例,所有客户端都通过这个实例来生成随机数。 实现单例模式的关键在于防止其他对象通过常规构造函数创建...

    单例模式详解~~单例模式详解~~

    然而,如果涉及类加载器或跨JVM的场景,单例模式的实现就需要更复杂的策略,例如使用`序列化`和`克隆`时需要特殊处理,防止生成额外的实例。另外,如果要考虑服务集群或分布式系统,可能需要采用分布式单例,例如...

Global site tag (gtag.js) - Google Analytics