文档章节

类加载器与单例

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

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的乒乓狂魔

© 著作权归作者所有

共有 人打赏支持
乒乓狂魔
粉丝 979
博文 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();这一点我有点理解不了,博主知道为什么吗?
单例类在多个classLoader里怎么保持唯一性的问题

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

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

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

蠢廿
2017/12/04
0
0
设计模式-单例模式

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

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

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

hafiz.zhang
2017/09/27
0
0
Singleton 单例模式

动机 有些情况下,一个类只能有一个实例是很重要的。比如说,在操作系统中只能有一个窗口管理器的(文件系统或打印机程序)。通常, 单实例用于对内部或外部资源的集中式管理,同时它们提供一...

holysu
2017/07/06
0
0
漫画:如何写出更优雅的单例模式?

上一次为大家介绍了单例模式的基本概念和几种实现方式,没看过的小伙伴们可以点击下面链接: 漫画:什么是单例设计模式? 如果懒得去看也不要紧,让我们来简单回顾一下。 线程安全的懒汉型单...

bjweimengshu
2017/12/04
0
0
Java基础知识学习(Java中有哪几种引用 & 最佳单列模式分析)

1、Java中有哪几种引用?它们的含义和区别是什么? 2、请用Java实现一个线程安全且高效的单例模式。 (1)利用了类加载机制来保证只创建一个instance实例,只要应用中不使用内部类,JVM就不会去...

Kael_祈求者
01/03
0
0
Python 中的单例模式

单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用...

funhacks
2017/01/17
0
0
菜鸟成长系列-单例模式

菜鸟成长系列-概述 菜鸟成长系列-面向对象的四大基础特性 菜鸟成长系列-多态、接口和抽象类 菜鸟成长系列-面向对象的6种设计原则 设计模式中总体分为三类: 一、创建型(5): 工厂方法[Factor...

glmapper
2017/12/18
0
0
Android设计模式探讨 单例模式

核心原则:将构造函数私有化,通过一个静态内部方法来获取唯一实例. 单例模式的定义:确保某个类只有一个实例,避免产生多个对象来消耗过度资源。 下面介绍几种常见实现单例模式的方法。 饿汉...

wongstar
2017/12/14
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

线程安全,底层实现原理和JMM

让多线程下的类安全起来:无状态、加锁、让类不可变、栈封闭(方法封装) 、安全的发布对象(不暴露成员) 死锁 一定发生在多个线程争夺多个资源里的情况下,发生的原因是每个线程拿到了某个(某...

这很耳东先生
5分钟前
0
0
MyBatis源码解读之延迟加载

1. 目的 本文主要解读MyBatis 延迟加载实现原理 2. 延迟加载如何使用 Setting 参数配置 设置参数 描述 有效值 默认值 lazyLoadingEnabled 延迟加载的全局开关。当开启时,所有关联对象都会延...

无忌
12分钟前
0
0
javascript 类变量的实现

代码如下: function echo(){ for(let i=0;i<arguments.length;i++) console.log(arguments[i]);}function extend(o, p){for (prop in p) {o[prop] = p[prop]}retur......

backbye
16分钟前
0
2
编程语言对比分析:Python与Java和JavaScript(图)

编程语言对比分析:Python与Java和JavaScript(图): 凭什么说“Python 太慢,Java 太笨拙,我讨厌 JavaScript”?[图] 编程语言生而为何? 我们人类从原始社会就是用语言表达自己,互相沟通...

原创小博客
24分钟前
0
0
Akka构建Reactive应用《one》

看到这Akka的官网,描述使用java或者scala构建响应式,并发和分布式应用更加简单,听着很高级的样子,下面的小字写着消息驱动,但是在quickstart里面又写容错事件驱动,就是这么钻牛角尖。 ...

woshixin
36分钟前
0
0
ffmpeg源码分析 (四)

io_open 承接上一篇,对于avformat_open_input的分析还差其中非常重要的一步,就是io_open,该函数用于打开FFmpeg的输入输出文件。 在init_input中有这么一句 if ((ret = s->io_open(s, &s-...

街角的小丑
37分钟前
0
0
String,StringBuffer ,StringBuilder的区别

不同点 一、基类不同 StringBuffer、StringBuilder 都继承自AbStractStringBuilder,String 直接继承自 Object 2、底层容器“不同” 虽然底层都是字符数组,但是String的是final修饰的不可变...

不开心的时候不要学习
52分钟前
0
0
nodejs 文件操作

写文件code // 加载文件模块var fs = require("fs");var content = 'Hello World, 你好世界!';//params 文件名,内容,编码,回调fs.writeFile('./hello.txt',content,'utf8',function (er......

yanhl
55分钟前
0
0
SpringBoot mybits 查询为0条数据 但是在Navicat 中可以查询到数据

1.页面请求: 数据库查询: 2018-07-16 17:56:25.054 DEBUG 17312 --- [nio-9010-exec-3] c.s.h.m.C.selectSelective : ==> Preparing: select id, card_number, customer_id, customer_nam......

kuchawyz
今天
0
0
译:Self-Modifying cod 和cacheflush

date: 2014-11-26 09:53 翻译自: http://community.arm.com/groups/processors/blog/2010/02/17/caches-and-self-modifying-code Cache处在CPU核心与内存存储器之间,它给我们的感觉是,它具......

我叫半桶水
今天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部