Java内置类加载器
java内置了三层次结构的类加载器
1:启动类加载器也叫引导类加载器 (Bootstrap) 加载JAVA_HOME/lib 下的类 比如rt.jar(加载系统jar包,对程序不可见的)
2:扩展类加载器(ExtClassLoader) 加载JAVA_HOME/lib/ext 下的类
3:应用程序类加载器(AppClassLoader) 加载应用程序CLASSPATH下的class文件
一个加载器在加载一个类的时候,都要先使用它的父类加载器去加载这个类,如果它的父类加载器可以加载这个类,
那么就用父类加载器加载这个类,否则自己加载这个类。
下面看一段程序:
- package com.lyz.classloader.test;
- /**
- * Java类加载器
- * @author liuyazhuang
- *
- */
- public class Test {
- public static void main(String[] args) {
- //应用类加载器 Test在classpath下 所以AppClassLoader会加载这个类
- System.out.println(Test.class.getClassLoader().getClass().getName());
- //启动类加载器 System类是rt.jar 包下的类 是启动类加载器加载的,但是启动类加载器对程序不可见,得到的是null
- System.out.println(System.class.getClassLoader());
- //扩展类加载器结构
- ClassLoader loader = Test.class.getClassLoader();
- while (null != loader) {
- System.out.println(loader.getClass().getName());
- loader = loader.getParent();
- }
- System.out.println(loader);
- }
- }
- sun.misc.Launcher$AppClassLoader
- null
- sun.misc.Launcher$AppClassLoader
- sun.misc.Launcher$ExtClassLoader
- null
自定义类加载器
自定义的类加载器 继承ClassLoader,推荐只重写findClass方法,这样就维持了加载器的父类委派机制。它的父类加载器默认为AppClassLoader。- package com.lyz.classloader.test;
- import java.io.ByteArrayOutputStream;
- import java.io.File;
- import java.io.FileInputStream;
- /**
- * 自定义类加载器
- * @author liuyazhuang
- *
- */
- public class MyClassLoader extends ClassLoader {
- // 类存放的路径
- private String classpath;
- public MyClassLoader(String classpath){
- this.classpath = classpath;
- }
- /**
- * 重写findClass方法
- */
- @Override
- public Class<?> findClass(String name) {
- byte[] data = loadClassData(name);
- return this.defineClass(name, data, 0, data.length);
- }
- public byte[] loadClassData(String name) {
- System.out.println("加载"+name); //注意这里的输出
- try {
- name = name.replace(".", "//");
- FileInputStream is = new FileInputStream(new File(classpath + name + ".class"));
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- int b = 0;
- while ((b = is.read()) != -1) {
- baos.write(b);
- }
- is.close();
- return baos.toByteArray();
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
- }
- //验证一下父类委派机制.
- @Test
- public void testParentLoader() throws ClassNotFoundException{
- //com.load.Test 此类在classpath下
- MyClassLoader loader = new MyClassLoader("D:\\Workspaces\\MySelf\\Test\\bin\\");
- Class<?> loadClass = loader.loadClass("com.lyz.classloader.test.MyClassLoaderTest");
- System.out.println(loadClass.getClassLoader());
- MyClassLoader loader1 = new MyClassLoader("D:\\Workspaces\\MySelf\\Test\\bin\\");
- Class<?> loadClass1 = loader1.loadClass("com.lyz.classloader.test.MyClassLoaderTest");
- System.out.println(loadClass1.getClassLoader());
- }
运行结果如下:
- sun.misc.Launcher$AppClassLoader@2a139a55
- sun.misc.Launcher$AppClassLoader@2a139a55
加载classpath之外的类,在d盘下新建一个类,如下:
- package com.lyz.classloader.test;
- /**
- * 测试在classpth以外的类
- * @author liuyazhuang
- *
- */
- public class ClassOutOfClasspath {
- static{
- System.out.println("测试在classpath以外的类");
- }
- public void say(){
- System.out.println("我是在classpath以外的类");
- }
- }
- @Test
- public void testDiffLoader() throws InstantiationException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
- MyClassLoader myLoader = new MyClassLoader("D:/");
- Class<?> clazz = myLoader.loadClass("com.lyz.classloader.test.ClassOutOfClasspath");
- Class<?> clazz1 = myLoader.loadClass("com.lyz.classloader.test.ClassOutOfClasspath");
- System.out.println(clazz == clazz1);
- System.out.println(clazz.getClassLoader());
- System.out.println(clazz1.getClassLoader());
- System.out.println("================");
- MyClassLoader myLoader1 = new MyClassLoader("D:/");
- Class<?> clazz2 = myLoader1.loadClass("com.lyz.classloader.test.ClassOutOfClasspath");
- System.out.println(clazz2 == clazz1 );
- System.out.println(clazz2.getClassLoader());
- System.out.println("================");
- Object newInstance = clazz.newInstance();
- Method declaredMethod = clazz.getDeclaredMethod("say", new Class[]{});
- declaredMethod.invoke(newInstance, new Object[]{});
- }
- 加载com.lyz.classloader.test.ClassOutOfClasspath
- true
- com.lyz.classloader.test.MyClassLoader@5010be6
- com.lyz.classloader.test.MyClassLoader@5010be6
- ================
- 加载com.lyz.classloader.test.ClassOutOfClasspath
- false
- com.lyz.classloader.test.MyClassLoader@7daf6ecc
- ================
- 测试在classpath以外的类
- 我是在classpath以外的类
tomcat也自定义了类加载器。每个部署的tomcat下的项目都使用不同的类加载器来加载,所以项目与项目之间的类是不可共用的 。这也是自定义类加载器的用处之一。其实tomcat中的类加载器分为
1:catalina加载器
2:公共类加载器
3:分享类加载器
对tomcat类加载器有兴趣的同学,可以深入看一下tomcat源码。