这几天在看ClassLoader,推荐一篇讲ClassLoader的文章,地址:http://longdick.iteye.com/blog/442213,然后又看了下《深入java虚拟机》里的ClassLoader章节,下面就随便说点,主要还是以练习代码为主。
1.对于任意一个类,由加载它的ClassLoader和它本身决定了在java虚拟机中的唯一性。
也就是说比较2个类,只有它们都是由同一个ClassLoader加载,那么比较才有意义。否则,即使是同一个类文件,如果加载它们的ClassLoader不同,那么这2个类必定不相等。
2.类加载的双亲委派机制是可以破坏的,通过改变CallClassLoader和ContextClassLoader。
被当前类引用的类的加载也是由加载当前类的ClassLoader加载,子线程的ContextClassLoader是由父线程的ContextClassLoader派生出来。
3.扩展ClassLoader一般应该建议重写findClass(String)方法。
原因是自定义的ClassLoader只专注于加载自己的Class,在加载自己的Class的过程中,又会先加载父类Object,Object类是由启动类加载器加载的,默认可以直接由loadClass(String)执行,这么做可以用ClassLoader来选择性加载某一些类做隔离。
贴代码
自定义ClassLoader,重写loadClass()。
@Override public Class<?> loadClass(String name) throws ClassNotFoundException { try { String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class"; InputStream is = getClass().getResourceAsStream(fileName); if (is == null) { return super.loadClass(name); } byte[] b = new byte[is.available()]; is.read(b); return defineClass(name, b, 0, b.length); } catch (Exception e) { throw new ClassNotFoundException(name); } }
测试类
public void say() { System.out.println("hello world"); } public static void main(String[] args) throws Exception { // 打印java虚拟机的ClassLoader System.out.println(Thread.currentThread().getContextClassLoader()); System.out.println(Thread.currentThread().getContextClassLoader().getParent()); System.out.println(Thread.currentThread().getContextClassLoader().getParent().getParent()); // 自定义ClassLoader ClassLoader myLoader = new MyClassLoader(); Class<?> clazz = myLoader.loadClass("com.zoo.classloader.ClassLoaderTest"); Object obj = clazz.newInstance(); // 打印自定义ClassLoader加载的Class对象 System.out.println(obj.getClass()); // 打印被加载的Class对象是由哪个ClassLoader加载的 System.out.println(obj.getClass().getClassLoader()); /* * 对于任意一个类,由加载它的ClassLoader和它本身决定了在jvm虚拟机中的唯一性。 * 也就是说比较2个类,只有它们都是由同一个ClassLoader加载,那么比较才有意义。 * 否则,即使是同一个类文件,只要加载它们的ClassLoader不同,那么这2个类必定不相等。 */ System.out.println(obj instanceof com.zoo.classloader.ClassLoaderTest); // 由自定义ClassLoader加载后,在程序里运行。 Method method = clazz.getDeclaredMethod("say", new Class<?>[] {}); method.invoke(obj, new Object[] {}); // 获取当前上下文的ClassLoader System.out.println(Thread.currentThread().getContextClassLoader()); // 改变上下文的ClassLoader Thread.currentThread().setContextClassLoader(myLoader); // 获取当前上下文的ClassLoader System.out.println(Thread.currentThread().getContextClassLoader()); // 改变当前上下文的ClassLoader可以改变在当前线程派生出的子线程的上下文ClassLoader Thread t = new Thread(new Runnable() { @Override public void run() { Thread t2 = Thread.currentThread(); System.out.println(t2.getName() + ":" + t2.getContextClassLoader()); } }); t.start(); Thread.sleep(3000); // 获取CallClassLoader Class<?> clz = Class.forName("com.zoo.classloader.ClassLoaderTest"); System.out.println(clz); System.out.println(clz.getClassLoader()); System.out.println(clz.getClass()); System.out.println(clz.getClass().getClassLoader()); }
执行结果:
sun.misc.Launcher$AppClassLoader@addbf1 ==> 当前上下文类加载器是应用类加载器
sun.misc.Launcher$ExtClassLoader@42e816 ==> 应用类加载器的父类加载器是扩展类加载器
null ==> 扩展类加载器的父类加载器是启动类加载器,因为启动类加载器是C++编写,java里获取不到
class com.zoo.classloader.ClassLoaderTest ==> 自定义类加载器加载进行加载
com.zoo.classloader.MyClassLoader@1fb8ee3 ==> 通过Class对象获取它的类加载器
false ==> 同一个Class,不同的类加载器加载,那么Class不相等
hello world ==> 被自定义类加载器加载的Class在程序中执行
sun.misc.Launcher$AppClassLoader@addbf1 ==> 获取当前上下文类加载器
com.zoo.classloader.MyClassLoader@1fb8ee3 ==> 设置上下文类加载器
Thread-0:com.zoo.classloader.MyClassLoader@1fb8ee3 ==> 派生出来的子线程的类加载器
class com.zoo.classloader.ClassLoaderTest ==> 默认加载类
sun.misc.Launcher$AppClassLoader@addbf1 ==> 默认加载用的是应用类加载器
class java.lang.Class ==> 获取Class
null ==> 获取类加载器,由于是启动类加载器,所以为null,java基础类是由启动类加载器加载。