java中的类加载器主要有三种:
引导类加载器
扩展类加载器
应用类加载器
通过代码了解类加载器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public class TestJDKClassLoader { public static void main(String[] args) { //打印类加载器 System.out.println(String.class.getClassLoader()); //不是java对象,打印null System.out.println(DESKeyFactory.class.getClassLoader()); System.out.println(TestJDKClassLoader.class.getClassLoader()); //类加载器之间的关系 System.out.println(); ClassLoader appClassLoader = ClassLoader.getSystemClassLoader(); ClassLoader extClassLoader = appClassLoader.getParent(); ClassLoader bootstrapLoader = extClassLoader.getParent(); System.out.println(appClassLoader); System.out.println(extClassLoader); System.out.println(bootstrapLoader);
System.out.println(); System.out.println("bootstrapLoader加载以下文件:"); URL[] urLs = Launcher.getBootstrapClassPath().getURLs(); for (URL url : urLs) { System.out.println(url); }
System.out.println(); System.out.println("extClassLoader加载以下文件:"); System.out.println(System.getProperty("java.ext.dirs"));
System.out.println(); System.out.println("appClassLoader加载以下文件"); System.out.println(System.getProperty("java.class.path"));
} }
|
双亲委派机制
JVM在进行类加载时有个双亲委派机制,当它需要去加载类时,自己先不去加载,而是委托给父加载器加载,父加载器又直接委派它的父加载器,直到找到引导类加载器时,他才会去对类进行加载,这时候如果加载不到,也就是在它所管辖的范围内找不到这个类,就会进行委托的退回,说,啊,我不行,还是你自己来吧,这样一直退回到能加载这个类的时候,才会对类进行加载。
总结起来就是所有类都会优先使用最顶层的类加载器去加载,从而保证了系统的安全性和类加载的唯一性。
比如我是一个黑客,在用户的系统里写了一个String类,这个类里面做了一些危害系统的代码,这时候双亲委派机制就能有效的避过这个类,他是应用类加载器要加载String时,会委派给上层,上层在委派给上上层,一直到引导类加载器,引导类加载器会直接使用Java自带的String类。
全盘负责委托机制
“全盘负责”是指当一个ClassLoder装载一个类时,除非显式的使用另外一个ClassLoder,否则该类所依赖及引用的类也由这个ClassLoder载入
自定义类加载器
自定义类加载器只需要继承java.lang.ClassLoader
类,该类有两个核心方法,一个是loadClass(String, boolean)
,实现了双亲委派机制,还有一个方法是findClass
,默认实现是空方法,所以我们自定义类加载器主要是重写findClass
方法.
打破双亲委派机制主要就是通过重写loadClass
方法,去掉它的递归委派的代码。
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| public class MyClassLoaderTest { static class MyClassLoader extends ClassLoader { private String classPath; public MyClassLoader(String classPath) { this.classPath = classPath; } private byte[] loadByte(String name) throws Exception { name = name.replaceAll("\\.", "/"); FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class"); int len = fis.available(); byte[] data = new byte[len]; fis.read(data); fis.close(); return data; } protected Class<?> findClass(String name) throws ClassNotFoundException { try { byte[] data = loadByte(name); return defineClass(name, data, 0, data.length); } catch (Exception e) { e.printStackTrace(); throw new ClassNotFoundException(); } } protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { Class<?> c = findLoadedClass(name); if (c == null) { if (!name.startsWith("com.locaris")) { c = this.getParent().loadClass(name); } else { c = findClass(name); } sun.misc.PerfCounter.getFindClasses().increment(); } if (resolve) { resolveClass(c); } return c; }
} } public static void main(String args[]) throws Exception { MyClassLoader classLoader = new MyClassLoader("D:/test"); Class clazz = classLoader.loadClass("com.youren.jvm.User"); Object obj = clazz.newInstance(); Method method = clazz.getDeclaredMethod("sout", null); method.invoke(obj, null);
System.out.println(clazz.getClassLoader().getClass().getName()); } }
|