java类加载器<复习>

闲来无事,看源码的时候看到了这么一句:Thread.currentThread().getContextClassLoader()

觉得似懂非懂的,不就是类加载器吗!不就是线程上下文的类加载器吗!可是为什么要这么写呢?

于是乎百度之。。。找了半天也没有看到有人来解释下这个东东的。

然后搜索JDK的API发现java.lang.Thread不仅仅有getContextClassLoader而且还有setContextClassLoader,很明显这两个应该是互为补充的。

思考良久忽然明白了,现记录下。

首先得明白,JAVA为什么要有类加载器?
原因,两点:
1、为了安全,并且防止重复加载。
2、早期的applet的需求(貌似很多人这么说,反正我也搞不清楚。。我那个时候还不知道JAVA呢!)

java类加载使用了委托模型,简单点说就是当前类加载器先把加载类的权限交给其parent类(注:parent类不是它父类,而是它的父加载器),然后就这么一直网上交,直到最高的爷爷辈的,由最高的爷爷来先加载指定的类,这和我们国家的空如让梨整好相反的!这样的好处是什么呢 ?好处是每次都让最顶级的加载器去加载类,就不会重复加载类,或者有人说不重复加载类可以有别的方法来控制啊,那如果这个不算的话,安全性应该算,应为每次都是最顶级的加载器去加载的话,就不会出现比如用户自己能定义java.lang.Object这样的类的情况。
第二点就不解释了,只能说可能是吧!

java中的系统类加载器:
Bootstrap  //引导类加载器
sun.misc.Launcher$AppClassLoader@535ff48b   //classpath下的类加载器
sun.misc.Launcher$ExtClassLoader@40affc70   //ext扩展包类加载器
引导类加载器不是JAVA语言写的,其他2个是JAVA写的。
package classLoader;

public class ClassLoaderTree {
	public static void main(String[] args) {
		//每个JAVA类都有一个指向它的类加载器的引用
		ClassLoader classLoader = ClassLoaderTree.class.getClassLoader();
		while (classLoader != null) {
			System.out.println(classLoader);
			classLoader = classLoader.getParent();
		}
	}
}


//////////////////////////////////////////////
JAVA类加载器的作用可能还有很多。
然后重点说说之前提到的问题。
比如获取Test的类加载器可以这么写:Test.class.getClassLoader()   //说明每个JAVA类都与一个类加载器相关联
获取线程的类加载器这么写:Thread.currentThread().getContextClassLoader()  //说明每个线程都与一个类加载器相关联

你可以把类加载器看成是人体是奇经八脉。
怎么说JAVA却说到中国的医药学了?
嘿嘿,我是突然想出来的,应为我感觉类加载器到处都是,除了系统的类加载器以外其实还有很多用户自定义的类加载器。比如TOMCAT或其他的开源项目在需要的时候就必须实现自己的类加载器,如果用系统的类加载器肯定会有问题的。
如果TOMCAT跑你的WEB项目使用系统的类加载器那是相当危险的,你可以直接是无忌惮是操作系统的各个目录了。
所以实现自己的类加载器有一个好处就是:我可以限制你只能把类写在指定的地方,否则我不给你加载!比如TOMCAT。

其实从Main函数开始执行代码,看似我们没有手动启动任何的线程,其实JVM已经帮助我们系统了一个线程------main线程。所以你就算不起线程,你的代码也是跑在线程上面的,那么你用Test.class.getClassLoader() 得到的类加载器就相当于拿到了main线程的类加载器,而你用Thread.currentThread().getContextClassLoader() 拿到的类加载器很明显是当前执行线程的类加载器,于是可以总结,无论怎么写,你拿到的都是某个线程的类加载器。所以这两句看似不一样的代码其实某种程度上又具有一致性!
而setContextClassLoader估计就更加好理解了,说白了也就是改变指定线程的类加载器。

总结,没什么神奇的。
JAVA中的子线程默认继承父线程的类加载器。

======================================
最后引用IBM的一个例子,给出写自定义类加载器的例子:
package classLoader;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * 文件系统的类加载器
 * 要实现自己的类加载器必须继承ClassLoader
 * 然后重写findClass方法,最后通过调用父类的defineClass方法把类给定义出来。
 * 
 * @author  li
 *
 */
public class FileSystemClassLoader extends ClassLoader {
	private String rootDir;

	public FileSystemClassLoader(String rootDir) {
		super();
		this.rootDir = rootDir;
	}

	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		byte[] byteArray = getClassData(name);
		if (byteArray == null) {
			throw new ClassNotFoundException(name);
		} else {
			return defineClass(name, byteArray, 0, byteArray.length);
		}
	}

	private byte[] getClassData(String className) {
		String classPath = classNameToPath(className);
		byte[] buf = new byte[1024];
		ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
		InputStream in = null;
		try {
			in = new FileInputStream(classPath);
			while (in.read(buf) != -1) {
				byteArray.write(buf);
			}
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		} finally {
			if (in != null)
				try {
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
		return byteArray.toByteArray();
	}

	private String classNameToPath(String className) {
		StringBuilder buff = new StringBuilder();
		buff.append(rootDir).append(File.separatorChar)
				.append(className.replace('.', File.separatorChar))
				.append(".class");
		System.out.println(buff);
		return buff.toString();
	}
}


package classLoader;

public class Sample {
	private Sample instance;

	public void setInstance(Object instance) {
		System.out.println("setInstance");
		this.instance = (Sample) instance;
	}

}

package classLoader;

import java.lang.reflect.Method;

public class Test {
	static Class<?> t1Clz = null;
	static Class<?> t2Clz = null;

	public static void main(String[] args) throws InterruptedException {
		String classDir = "D:\\eclipse\\myTest\\web\\WEB-INF\\classes\\classLoader";
		/*
		 * 两个类加载器分别去加载指定的类,但这个类只能被加载一次
		 */
		FileSystemClassLoader fsc1 = new FileSystemClassLoader(classDir);
		FileSystemClassLoader fsc2 = new FileSystemClassLoader(classDir);
		final String className = "classLoader.Sample";
		try {
			Class<?> class1 = fsc1.loadClass(className);
			Object obj1 = class1.newInstance();
			Class<?> class2 = fsc2.loadClass(className);
			Object obj2 = class2.newInstance();
			System.out.println(class1 == class2);
			System.out.println(obj1 == obj2);

			Method method1 = class1.getMethod("setInstance",
					java.lang.Object.class);
			System.out.println(method1);
			method1.invoke(obj1, obj2);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}


你可能感兴趣的:(java类加载器<复习>)