当使用不同的类加载器时,也会使单例失效,如下:
单例为:
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的乒乓狂魔
分享到:
相关推荐
这里我们关注的是如何使用配置文件、类加载器以及单例模式来高效、稳定地管理数据库连接。Oracle数据库是一种广泛使用的商业关系型数据库系统,尤其适用于大型企业级应用。下面将详细阐述这些知识点。 首先,**配置...
3. 单例模式的实现:在多线程环境中,如果每个线程都有自己的类加载器,可能导致单例模式失效,因为每个加载器加载的类都是独立的实例。 2. 再分析类加载 类加载的过程包括三个主要步骤:加载(Loading)、验证...
通过实践操作,掌握类的加载、连接与初始化过程,了解不同类型的类加载器及其工作原理,学会创建自定义的类加载器,并对类的卸载有所认识。实验将结合具体的Java程序实例,运用单例模式对静态变量和对象进行初始化,...
2. **类加载器问题的解决**:为了解决不同类加载器加载同一个类产生的问题,可以在单例类中添加一个私有构造函数,并在构造函数中进行检查,确保每次加载的都是同一个实例。 3. **序列化问题的解决**:如果单例类...
这是最简单的单例实现方式,通过静态常量来存储唯一实例,保证在类加载时就完成初始化,因此线程安全。代码如下: ```java public class StaticSingleton { private static final StaticSingleton INSTANCE = new ...
2. **多类加载器协同**:当应用程序中有多个类加载器时,如何确保不同类加载器下仍然保持单例的唯一性成为问题。此时需要考虑如何让单例对象能够在不同的类加载器之间共享。 3. **跨JVM环境**:在集群环境中或者...
首先,类与数据在Java中的区别在于,类是执行的代码,包含了方法的定义和类的静态部分,而数据则是与类实例相关的状态,每个实例可以有不同的状态值。当创建一个类的实例时,就是将类的代码和特定的状态结合起来。在...
在`singleton-SoundManagerDemo`这个项目中,我们可以看到`SoundManager`的实现以及如何在其他视图控制器中使用这个单例。通常,你会在需要播放音频的地方调用`[SoundManager sharedManager]`来获取单例实例,然后...
另外,在测试时,单例类往往难以进行单元测试,因为它们没有公共的构造器,且通常会有一些全局状态,这会使得模拟这些对象变得复杂。 总的来说,单例模式虽然简单,但其设计和实现却蕴含着深刻的设计原则和考量,...
单例模式恰好可以帮助我们实现这一目标,因为单例的数据管理类可以缓存已加载的省级和市级数据,减少不必要的网络请求。 在具体实现上,我们可以使用JavaScript或者Java等编程语言,结合前端框架(如React、Vue)...
"Java基于自定义类加载器实现热部署过程解析" Java中基于自定义类加载器实现热部署是指在不重启应用的情况下,当类的定义即字节码文件修改后,能够替换该Class创建的对象。热部署是Java中的一个重要概念,它可以...
Java 类加载器会先加载静态变量,然后加载对象实例化,最后加载成员变量和方法。 单例模式是一种常见的设计模式,它确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。懒汉式单例是一种常见的单例...
### 使用Java单例模式实现一个简单的日志记录器 #### 一、单例模式简介 单例模式是一种常用的软件设计模式,在该模式中,一个类只能创建一个实例,并且提供了一个全局访问点来访问该实例。单例模式的主要优点包括...
在 Egret 中,我们可以创建一个名为 `SceneManager` 的类,用作场景管理器。这个类将包含对所有场景的控制,包括加载、切换和卸载场景。为了实现单例,我们需要: 1. 将构造函数设为私有,防止外部直接实例化。 ```...
类型加载时初始化,与静态常量方式类似,但避免了不必要的内存分配。 ```csharp public class Singleton { private static readonly Singleton instance = new Singleton(); private Singleton() {} ...
- 双亲委派模型是指在类加载过程中,如果一个类加载器收到了类加载请求,它首先不会自己尝试去加载这个类,而是把类加载请求委托给父类加载器完成。只有父类加载器无法完成加载时,才会尝试自己加载。 2. **Spring...
在描述中提到的随机数生成器就是一个典型的单例应用,它确保在整个应用程序运行期间,只有一个随机数生成器实例,所有客户端都通过这个实例来生成随机数。 实现单例模式的关键在于防止其他对象通过常规构造函数创建...
然而,如果涉及类加载器或跨JVM的场景,单例模式的实现就需要更复杂的策略,例如使用`序列化`和`克隆`时需要特殊处理,防止生成额外的实例。另外,如果要考虑服务集群或分布式系统,可能需要采用分布式单例,例如...