自定义加载器,加载重复的calss和rt.jar 下的class

一、序言

        先啰嗦一下,上次,阿里面试的时候问到能否加载一个java.lang.xx 的类,我的回答的是不能- -!当然答案是正确的,但是不知道为什么。

        还有一个问题:如果加载两个jar,里面含有相同路径的类,是可以的吗?我还是回答不能- -,估计会冲突,但是原因也是模模糊糊,这里我再回顾一自定义加载类的方法吧,至于原理和细节的介绍,在JVM 目录下有。

 

 

二、自定义类加载器:

       这里还是先写一个简单的加载器,方便测试吧!

       

/**
 * 自定义加载器的工具类
 * 临时写的,暂时不做过多控制
 * @author Ran
 *
 */
public class ClassLoaderUtils extends ClassLoader{
	// 获取二进制字节流
	private byte[] getData(InputStream in){
		byte[] data = null;
		try {
			data = new byte[in.available()];
			in.read(data);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			try {
				if(in != null){
					in.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return data;
	}
	
	// 获得文件流
	private InputStream getInputStream(String name){
		name = name.replace(".", "/")+".class";
		return getClass().getClassLoader().getResourceAsStream(name);
	}
    
	
	// 这个源码是直接抛出异常,并且是protected,好让我们重写findClass
	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		InputStream in = getInputStream(name);
		if(in == null){
			System.out.println("自定义加载失败!");
			return super.loadClass(name);
		}
		byte[] data = this.getData(in);  
		return super.defineClass(name,data, 0, data.length);
	}
	
	//  测试
	public static void main(String[] args) throws Exception, 
        IllegalAccessException, ClassNotFoundException {
                // 
		String name = "com.T1";
		ClassLoaderUtils cu = new ClassLoaderUtils();
		System.out.println(cu.findClass(name).newInstance());
	}
}

 

   2.1  尝试加载java.lang.下面的类,这里我创建了一个 java.lang.LangTest 类,JDK1.6 进行测试。

     

//  测试
	public static void main(String[] args) throws Exception, IllegalAccessException, ClassNotFoundException {
		String name = "java.lang.LangTest";
		ClassLoaderUtils cu = new ClassLoaderUtils();
		System.out.println(cu.findClass(name).newInstance());
	}

    结果:Exception in thread "main" java.lang.SecurityException: Prohibited package name: java.lang

     

   后面我继续测试,我同样用rt.jar 下面的目录结构,结果是:

   java.io.xxx :异常同上

   sun.nio.xxx: 没有异常。

   也就说我们猜想,自定义加载器在加载rt.jar 里面,包含java 开头的会出错,从错误分析,有包名检测机制决定的。java 包下面的应该属于很核心的东西,不允许我们建立这些。

 

   2.3 同一包下面的类,可以被多次加载吗?

   

		String name = "com.Test3";
		ClassLoaderUtils cu = new ClassLoaderUtils();
		Object a = cu.findClass(name);
		System.out.println(a);
		Object b = cu.findClass(name);
		System.out.println(b);

   结果:

        loader (instance of  ClassLoaderUtils): attempted  duplicate class definition for name: "com/Test3"

        可以看出:不能重复加载,我们换种方式呢?

   

	String name = "com.Test3";
		ClassLoaderUtils cu = new ClassLoaderUtils();
		Object a = cu.findClass(name);
		System.out.println(a);
                // 从新创建一个加载器
		ClassLoaderUtils cu1 = new ClassLoaderUtils();
		Object b = cu1.findClass(name);
		System.out.println(b);

 

    这个结果是正常的。

    我们可以猜想:同一个加载器无法加载相同包的的同名类,在类加载机制里面,我们说到每个类在内存中只有一份,这里的一份是指拥有相同的类加载器的情况。因此很多第三方框架都喜欢用自己的类加载器,不至于重复。

 

 小结:

       1.关于类加载的东西,我是有些模糊的,比如第一个问题,我仅仅知道java.lang 下的不能自己加载,知道那是核心的东西,但是具体的原因,以及是实现过程,是没有研究的,比较清楚的大哥,希望可以介绍介绍。

       2.关于类在内存中只有一份,这个概念,应该是规范要求的,毕竟我们存在积分相同的类是没意义的,会破坏我们的继承机制,比如有两份java.lang.Object 类,我们的子类到底属于哪个? 这里我尝试过,即使编译也通不过!

 

      3.关于一个类多次加载的问题,我用 jar cvf  打包名.jar 目标.class 的命令打成两个不同的jar,然后放入工程,是没有错误的,但是只能调用其中一个类的方法, 比如a.jar 里面打印1,b.jar 打印2,那么我调方法的时候是打印的1,没有明确先后,都是App 的加载器。

 

      4.上面仅仅是验证下效果,具体的原理没有详细分析,动动手,自己放心,详细的后面慢慢研究。

    

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值