通过一个类的全限定名来获取描述此类的二进制字节流,这个动作放到虚拟机外部去实现,以便让程序自己决定如何去获取所需要的类。这个模块的动作成为“类加载器”。
唯一性:对于任意一个类,必须类的加载器和类本身,同时来确立其唯一性,每一个类加载器,都拥有一个独立的类的名称空间。
比较两个类是否相等,只有在两个类是由同一个类加载器加载的前提下才有意义,否则即使两个类来源于同一个class 文件,被同一个虚拟机加载,只要加载器不同,那么他们两个类必定不同。这里的“相等”包括对象的equals(),isAssignableForm(),isInstance 方法的返回结果。可以代码简单演示:
public class ClassLoaderTest { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException { ClassLoader myLoder = new ClassLoader() { @Override public Class<?> loadClass(String name) throws ClassNotFoundException { String fileName = name.substring(name.lastIndexOf(".")+1)+".class"; InputStream in = getClass().getResourceAsStream(fileName); Class<?> c = null; try { if(in == null){ return super.loadClass(name); } byte[] b = new byte[in.available()]; in.read(b); c = defineClass(name, b, 0, b.length); } catch (Exception e) { e.printStackTrace(); } return c; } }; Object obj = myLoder.loadClass("com.ClassLoaderTest").newInstance(); System.out.println(obj.getClass()); System.out.println(obj instanceof com.ClassLoaderTest); } }
结果:
class com.ClassLoaderTest
false
返回false 是因为系统存在两个ClassLoaderTest,一个是系统默认加载的,一个是自己new 的加载器,虽然是用一个class 文件,但是依然是独立的两个类。而我们经常做equals判断的时候会先进行instanceof 检查,大多数情况都是true,因为统一是默认的加载器加载的,下面看看一些加载器的工作情况。
一、双亲委派模型
从JAVA虚拟机的角度来说,主要有两个加载器:一种是启动了加载器
(Bootstrap ClassLoader),这是由C++写的,是虚拟机一部分,另一种是其他类的加载器,是有Java实现的,独立于虚拟机外,并且都继承自抽象类java.lang.ClassLoader.细分一下,可以分为扩展类加载器(Extension ClassLoader)和应用程序加载器(Application ClassLoader)。
1.启动类加载器:主要负责加载<JAVA_HOME>\lib 目录中的,虚拟机能识别的文件名的文件。这个加载器无法没Java程序直接用。
2.扩展类加载器:主要负责加载<JAVA_HOME>\lib\ext下的,或者被java.ext.dirs 系统变量指定的类库,开发者可以直接使用的。
3.应用类加载器:主要负责加载用户类路径(classpath)上所指定的类库,程序中默认的类加载器,开发者可以自己进行使用。
这几个加载器在rt.jar sun.misc.Launcher 下,这里我还进不去..!他们直接由上到下的关系,就是双亲委派模型,关于这个模型定义,这里大概介绍一下,它要求除了启动类加载器以外,其他都需要自己的父类加载器,并且父子关系不是继承,而是组合,也就是说类的加载会先让最上面的进行加载,加载不了的,再让后面的进行加载,你会发现每次都会从启动类加载器开始,而且object 这个最大的父类就在rt.jar 里面,也是有启动类加载器进行加载的。这种模式是JDK推荐的,因为如果不按这种方式,自己写个Object 类,自己定义的位置进行加载,那么会混乱。
对于双亲委派模型代码都集中在java.lang.ClassLoader 的loadClass 中,可以通过图和源码进行了解:
图参考:http://yueyemaitian.blog.163.com/blog/static/2165043320097211052534/
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { //1. 先检查是否被加载过了 Class c = findLoadedClass(name); if (c == null) { try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClass0(name); } } catch (ClassNotFoundException e) { // 2. 如果父类找不到,就抛出ClassNotFoundException // 默认直接抛出异常,JDK要求重写这个方法 c = findClass(name); } } if (resolve) { resolveClass(c); } return c; }
注:JDK 建议我们在实现自己的加载器的时候,不要覆盖locaClass方法,而是建议我们去重写findClass 方法,这样能保证双亲委派模型,同时也实现了自己的方法。
双亲委派模型比较稳定,能很好的解决类加载的基础类问题(越基础的类,越由上层加载器进行记载),这些了之所以叫"基础",因为他们总是被用户代码调用API,但是在某些时候,这些基础类又要会调回用户代码,怎么办呢?
比如:用户调用基础类的method-1 方法,而该方法又要调用用户自己写的method-2,这类引用场景一般会在API写了接口,或者一种实现方法,而我们调的时候需要实现自己的方法,比如JDBC,JDNI,提供了接口,具体的实现需要各个不同的数据库厂商去实现,这里就是不基本的双亲委派模型了。
JNDI现在放在rt.jar 里面,目的是对资源进行查找和集中管理,他需要独立厂商去实现并部署,而启动类加载器不识别这些代码,怎么办呢?
Java 团队又设计了一个上下文类加载器(Thread Context ClassLoader),可以通过java.lang.Thread 的setContextClassLoder 进行设置,如果创建线程时还未设置,它将会从父线程中继承一个,如果程序全局都没设置过,那么默认还是App应用程序加载器。
有了线程上下文加载器,就能加载厂商独立实现的代码了,也就是父类加载器请求子类加载器去完成类的加载动作,双亲委派模型是父类先加载,加载不了再给子类加载,而这里是父类直接请求子类加载,已经破坏了委派模型层次。其中JCE、JAXB、JBI都也都采用了。
其次,用户在追求程序动态性的时候,希望程序想USB 接口一样,可以不用重启电脑,而更换各种外接东西。为了完成这个诱人的操作,比如OSGI实现了模块化热部署,它的关键是自己定义类加载器的实现机制,每一个模块都有自己的类加载器,需要更换模块的时候,就把模块和类加载器一起换掉,实现热替换。(ps:这玩意儿我也没用过,感觉就像webservice 远程调用那样,规定了接口,客户端固定,然后可以随意的替换服务端,从而客户端获得不同的输出)
关于osgi 的原理以及机制,可以参考<深入理解osgi>..之类的书。
小结:
1.这里主要介绍了类加载器的种类以及运行原理,方便我们加深对类的理解
2.这些都参考JVM里面的东西,写成小章节,方便自己理解,也分享一部分经验。
相关推荐
总结,Java 类加载器是JVM中的重要组成部分,它决定了类的加载过程和加载源,双亲委派机制保证了类加载的有序性和安全性。理解类加载器的工作原理有助于我们更好地进行程序设计和优化,特别是在开发插件系统、模块化...
2. **类加载器及类加载器的委托机制**:JVM中有三种内置的类加载器,分别是启动类加载器、扩展类加载器和应用类加载器。此外,还可以自定义类加载器。类加载器之间遵循委托机制,即下级类加载器先请求上级类加载器...
例如,当我们尝试加载 `java.lang.Object` 类时,首先会由启动类加载器加载,如果它找不到,会继续交给扩展类加载器,接着是系统类加载器,最后才会由用户自定义的类加载器尝试加载。 1.3 类加载双亲委派示例 为了...
本部分我们将深入探讨JVM中的类加载器,特别是根类加载器、扩展类加载器和系统类加载器。 首先,让我们了解类加载的基本过程。当JVM启动时,会触发类加载。这个过程分为三个阶段:加载、链接和初始化。加载阶段,类...
Java代码执行流程是JVM的核心流程之一,它首先通过编译器把Java代码转换成字节码,然后通过类加载器加载到内存中,并将其放在运行时数据区的方法区内。最后,执行引擎将字节码翻译成底层系统指令,再交由CPU去执行。
JVM类加载器 test
类加载器是JVM实现动态加载的核心组件,它的主要任务是根据类名找到对应的.class文件,并将其转换为内存中的Class对象。Java中的类加载器主要有以下几种: 1. Bootstrap ClassLoader:引导类加载器,负责加载JRE...
JVM类加载跟踪器,用于排查jar包冲突、类冲突、类版本冲突、NoClassDefFoundError、ClassNotFoundException 等等类加载相关问题的辅助工具
Java虚拟机(JVM)的类加载器子系统是Java运行时环境的一个重要组成部分,它负责将.class文件加载到内存中,并生成对应的Java类对象。这一过程涵盖了从文件系统或网络获取.class文件、验证类文件的正确性、准备类...
在Java世界中,类加载器(ClassLoader)是关键组件,它们负责将类的字节码加载到Java虚拟机(JVM)中。JVM、OSGI(Open Service Gateway Initiative)和Tomcat等容器都涉及到了类加载器的概念,理解它们的工作原理对...
- **直接引用**: 当一个类直接引用另一个类时(例如,类A引用类B),JVM会使用加载类A的类加载器来加载类B。 - **反射调用**: 使用`Class.forName()`方法也会触发类的加载。 - **初始化**: 当类的静态块或静态成员被...
JVM使用类加载器(ClassLoader)来完成这一任务。在Java中,每个类都由一个对应的ClassLoader实例负责加载。默认的类加载器包括Bootstrap ClassLoader(引导类加载器)、Extension ClassLoader(扩展类加载器)和...
在Java中,类加载器(Class Loader)是一项核心机制,用于将字节码(.class文件)加载到JVM中,使其成为运行时的对象。类加载器不仅实现了类的加载功能,还确保了Java程序在多线程环境中的安全性和隔离性。类加载器...
这些内置的类加载器加载的类在JVM生命周期中不会被卸载。而用户自定义的类加载器加载的类,当它们的`Class`对象不再被引用时,可能会被卸载,以节省内存资源。 了解类加载机制对于优化性能、理解和解决类加载相关...
Java中的类加载器是JVM(Java虚拟机)的核心组件之一,它们负责将.java源代码编译成的.class字节码文件加载到JVM中,从而使得程序能够运行。类加载器不仅涉及到程序的正常执行,还与Java的动态加载、模块化系统以及...
通过对Java中JVM加载`.class`文件的过程及其类加载器的具体工作原理的介绍,我们可以更加深刻地理解Java程序的运行机制。了解这些基础知识对于开发高质量的Java应用程序至关重要。通过掌握类加载器的工作方式,...
在Java虚拟机(JVM)中,类加载器(ClassLoader)是负责将类的`.class`文件加载到内存中的重要组件。理解类加载器的工作原理对于深入掌握JVM以及解决实际开发中的问题至关重要。 **1.1 类加载器分类** 类加载器可以...
JVM的垃圾收集机制可以回收不再使用的对象,但类加载器却难以卸载。这是因为类加载器与类的关系是“父子关系”,而非“拥有关系”,只有当类加载器被回收,它所加载的类才会一同卸载。 6. **类加载器优化** 优化...
Java 类加载器是Java虚拟机(JVM)的核心组成部分,它负责将类的字节码加载到内存中并转换为可执行的Java类。类加载器的作用不仅仅是加载类,还包括确保类的唯一性,避免重复加载,并且遵循特定的加载顺序。以下是对...