本文共 6379 字,大约阅读时间需要 21 分钟。
类加载器就是用来加载类的东西!类加载器也是一个类:ClassLoader
类加载器可以被加载到内存,是通过类加载器完成的!主要分三类:
Java虚拟机中的所有类装载器采用具有父子关系的树形结构进行组织,在实例化每个类装载器对象时,需要为其指定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
将class文件读入内存,并为之创建一个Class对象。
分三个小阶段
类会在首次被“主动使用”时执行初始化,为类(静态)变量赋予正确的初始值。在Java代码中,一个正确的初始值是通过类变量初始化语句或者静态初始化块给出的。
两个步骤:
初始化接口并不需要初始化它的父接口
类初始化方式
类加载器 | 名称 | 作用 |
---|---|---|
BootstrapClassLoader | 根类加载器 | 负责Java核心类的加载,比如System,String等。在JDK中JRE的lib目录下rt.jar文件中。 |
ExtensionClassLoader | 扩展类加载器 | 负责JRE的扩展目录中jar包的加载。在JDK中JRE的lib目录下ext目录 |
SysetmClassLoader | 系统类加载器 | 负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。 |
一个classloader中不会出现相同的类,在不同的classloader中可以出现同名的类。
在JVM中,不可能存在一个类被加载两次的事情!一个类如果已经被加载了,当再次试图加载这个类时,类加载器会先去查找这个类是否已经被加载过了,如果已经被加载过了,就不会再去加载了。
但是,如果一个类使用不同的类加载器去加载是可以出现多次加载的情况的!也就是说,在JVM眼中,相同的类需要有相同的class文件,以及相同的类加载器。当一个class文件,被不同的类加载器加载了,JVM会认识这是两个不同的类,这会在JVM中出现两个相同的Class对象!甚至会出现类型转换异常!
首先委托类加载器的父类去加载,如果父类无法加载则自己加载
当系统类加载器去加载一个类时,它首先会让上级去加载,即让扩展类加载器去加载类,扩展类加载器也会让它的上级引导类加载器去加载类。如果上级没有加载成功,那么再由自己去加载!
例如
我们自己写的Person类,一定是存放到CLASSPATH中,那么一定是由系统类加载器来加载。当系统类加载器来加载类时,它首先把加载的任务交给扩展类加载器,如果扩展类加载器加载成功了,那么系统类加载器就不会再去加载。这就是代理模式了!
同理,扩展类加载器也会把加载类的任务交给它的“上级”,即引导类加载器,引导类加载器加载成功,那么扩展类加载器也就不会再去加载了。引导类加载器是用C语言写的,是JVM的一部分,它是最上层的类加载器了,所以它就没有“上级了”。它只负责去加载“内部人”,即JDK中的类,但我们知道Person类是我们自己写的类,所以它加载失败。
代理模式保证了JDK中的类一定是由引导类加载加载的!这就不会出现多个版本的类,这也是代理模式的好处。
如果对ClassLoader里的加载流程源码感兴趣可以自己看下源代码,或者参考下面的一篇博客
自定义的加载器需要继承ClassLoader类来完成自定义类加载器。 ClassLoader加载类都是通过loadClass()方法来完成的 。
ClassLoader类中的方法
方法 | 说明 |
---|---|
getParent() | 获取上级类加载器 |
loadClass() | 实现了类加载的加载流程,也就是算法框架 |
findLoadedClass() | 查看该类是否被加载过 |
findClass() | 真正去加载类,自定义类加载器需要重写的方法 |
defineClass() | 把Class的字节数组byte[]转成Class |
loadClass方法主要以下几个步骤:
所以,我们只需要重写 findClass 方法就可以了,而不用重写loadClass方法,会覆盖了原来的代理模式。
findClass 方法中又主要做了以下几件事:
源码
java.lang.ClassLoader#loadClass(java.lang.String, boolean)
protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } }
通过ClassLoader里的loadClass方法,可以看出三个步骤
自定义ClassLoader
public class FileSystemClassLoader extends ClassLoader { private String classPath; public FileSystemClassLoader(String classPath) { this.classPath = classPath; } @Override protected Class findClass(String name) throws ClassNotFoundException { try { byte[] datas = getClassByte(name); if (datas == null) { throw new ClassNotFoundException("类没有找到:" + name); } return this.defineClass(name, datas, 0, datas.length); } catch (IOException e) { e.printStackTrace(); throw new ClassNotFoundException("类找不到:" + name); } } private byte[] getClassByte(String packageFullName) throws IOException { // 将包里的.替换成\ String namePath = packageFullName.replaceAll(".", "\\") + ".class"; File file = new File(classPath, namePath); // 使用commons-io的FileUtils工具类将文件读取到内存byte[] return FileUtils.readFileToByteArray(file); }}
实体类
public class Student { private String name; private String info; public String doing(String something) { return "doing..." + something; }}
将Student.java类编译成class文件,放到一个目录下(跟下面测试类的路径一致)
测试类
public class FlieSystemClassLoaderTest { public static void main(String[] args) { ClassLoader loader = new FileSystemClassLoader("D:\\yuxlhome\\del"); try { Class clazz = loader.loadClass("com.iwork.java.base09.reflect.chapter01.Student"); Object obj = clazz.newInstance(); Method doingMethod = clazz.getMethod("doing", String.class); String result = (String) doingMethod.invoke(obj, "study"); System.out.println(result); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } }}
需要注册个人用户才能看到全部菜单
网站覆盖 “前端/后端/运维/测试” 常见技术的资料,为您提供丰富且全面的IT学习手册,不用再为找IT学习资料而烦恼。内容从入门到高级再深入源码,由浅入深,由点到面,为您提供一套完整IT学习路线图及学习手册,让您从小白到精通,由菜鸟到大神。只要您有一颗好学之心,就能助您早日成"神"。
我们也期待任何有意朋友能加入我们,一起打造一套完整且全面的IT学习手册,能给需要的人员提供一点思路或者帮助,为每一位"IT修神"之路的人,推一把力,助一次攻,留下一点足迹…
转载地址:http://czhws.baihongyu.com/