- 浏览: 192057 次
- 性别:
- 来自: 上海
-
文章分类
最新评论
本文主要总结一下我对Java类加载器(Class Loader)认识,如有不准确之处还望大侠不吝赐教!
关键字:Java,类加载器(Class Loader)
主要从如下几个部分进行介绍:
类加载的过程
类加载器的层级
类加载器的操作原则
为什么要个性化类加载器
其它相关问题
类加载的过程
类加载器的主要工作就是把类文件加载到JVM中。如下图所示,其过程分为三步:
加载:定位要加载的类文件,并将其字节流装载到JVM中;
链接:给要加载的类分配最基本的内存结构保存其信息,比如属性,方法以及引用的类。在该阶段,该类还处于不可用状态;
验证:对加载的字节流进行验证,比如格式上的,安全方面的;
内存分配:为该类准备内存空间来表示其属性,方法以及引用的类;
解析:加载该类所引用的其它类,比如父类,实现的接口等。
初始化:对类变量进行赋值。
类加载器的层级
下图虚线以上是JDK提供的几个重要的类加载器,详细说明如下:
Bootstrap Class Loader: 当启动包含主函数的类时,加载JAVA_HOME/lib目录下或-Xbootclasspath指定目录的jar包;
Extention Class Loader:加载JAVA_HOME/lib/ext目录下的或-Djava.ext.dirs指定目录下的jar包。
System Class Loader:加载classpath或者-Djava.class.path指定目录下的类或jar包。
需要了解的是:
除了Bootstrap Class Loader外,其它的类加载器都是java.lang.ClassLoader类的子类;
Bootstrap Class Loader不是用Java实现,如果你没有使用个性化类加载器,那么java.lang.String.class.getClassLoader()就为null,Extension Class Loader的父加载器也为null;
获得类加载器的几种方式:
获得Bootstrap Class Loader:试图获得Bootstrap Class Loader,得到的必然是null。可以用如下方式验证下:使用rt.jar包内的类对象的getClassLoader方法,比如java.lang.String.class.getClassLoader()可以得到或者获得Extention Class Loader,再调用getParent方法获得;
获得Extention Class Loader:使用JAVA_HOME/lib/ext目录下jar包内的类对象的getClassLoader方法或者先获得System Class Loader,再通过它的getParent方法获得;
获得System Class Loader:调用包含主函数的类对象的getClassLoader方法或者在主函数内调用Thread.currentThread().getContextClassLoader()或者调用ClassLoader.getSystemClassLoader();
获得User-Defined Class Loader:调用类对象的getClassLoader方法或者调用Thread.currentThread().getContextClassLoader();
类加载器的操作原则
代理原则
可见性原则
唯一性原则
代理原则
代理原则指的是一个类加载器在加载一个类时会请求它的父加载器代理加载,父加载器也会请求它的父加载器代理加载,如下图所示。
为什么要使用代理模式呢?首先这样可以减少重复的加载一个类。(还有其它原因吗?)
容易误解的地方:
一般会以为类加载器的代理顺序是Parent First的,也就是:
加载一个类时,类加载器首先检查自己是否已经加载了该类,如果已加载,则返回;否则请父加载器代理;
父加载器循环重复1的操作一直到Bootstrap Class Loader;
如果Bootstrap Class Loader也没有加载该类,将尝试进行加载,加载成功则返回;如果失败,抛出ClassNotFoundException,则由子加载器进行加载;
子类加载器捕捉异常后尝试加载,如果成功则返回,如果失败则抛出ClassNotFoundException,直到发起加载的子类加载器。
这种理解对Bootstrap Class Loader,Extention Class Loader,System Class Loader这些加载器是正确的,但一些个性化的加载器则不然,比如,IBM Web Sphere Portal Server实现的一些类加载器就是Parent Last的,是子加载器首先尝试加载,如果加载失败才会请父加载器,这样做的原因是:假如你期望某个版本log4j被所有应用使用,就把它放在WAS_HOME的库里,WAS启动时会加载它。如果某个应用想使用另外一个版本的log4j,如果使用Parent First,这是无法实现的,因为父加载器里已经加载了log4j内的类。但如果使用Parent Last,负责加载应用的类加载器会优先加载另外一个版本的log4j。(更多信息)
可见性原则
每个类对类加载器的可见性是不一样的,如下图所示。
扩展知识,OSGi就是利用这个特点,每一个bundle由一个单独的类加载器加载,因此每个类加载器都可以加载某个类的一个版本,因此整个系统就可以使用一个类的多个版本。
唯一性原则
每一个类在一个加载器里最多加载一次。
扩展知识1:准确地讲,Singleton模式所指的单例指的是在一组类加载器中某个类的对象只有一份。
扩展知识2:一个类可以被多个类加载器加载,每个类对象在各自的namespace内,对类对象进行比较或者对实例进行类型转换时,会同时比较各自的名字空间,比如:
Klass类被ClassLoaderA加载,假设类对象为KlassA;同时被ClassLoaderB加载,假设类对象为KlassB,那么KlassA不等于KlassB。同时ClassA的实例被cast成KlassB时会抛出ClassCastException异常。
为什么要个性化类加载器
个性化类加载器给Java语言增加了很多灵活性,主要的用途有:
可以从多个地方加载类,比如网络上,数据库中,甚至即时的编译源文件获得类文件;
个性化后类加载器可以在运行时原则性的加载某个版本的类文件;
个性化后类加载器可以动态卸载一些类;
个性化后类加载器可以对类进行解密解压缩后再载入类。
其它相关问题
类的隐式和显式加载
隐式加载:当一个类被引用,被继承或者被实例化时会被隐式加载,如果加载失败,是抛出NoClassDefFoundError。
显式加载:使用如下方法,如果加载失败会抛出ClassNotFoundException。
cl.loadClass(),cl是类加载器的一个实例;
Class.forName(),使用当前类的类加载器进行加载。
类的静态块的执行
假如有如下类:
[java] view plaincopy
package cn.fengd;
public class Dummy {
static {
System.out.println("Hi");
}
}
另建一个测试类:
[java] view plaincopy
package cn.fengd;
public class ClassLoaderTest {
public static void main(String[] args) throws InstantiationException, Exception {
try {
/*
* Different ways of loading.
*/
Class c = ClassLoaderTest.class.getClassLoader().loadClass("cn.fengd.Dummy");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
运行后效果如何呢?
不会输出Hi。由此可见使用loadClass后Class类对象并没有初始化;
如果在Load语句后加上c.newInstance(); 就会有Hi输出,对该类进行实例化时才初始化类对象。
如果换一种加载语句Class c = Class.forName("cn.fengd.Dummy", false, ClassLoader.getSystemClassLoader());
不会输出Hi。因为参数false表示不需要初始化该类对象;
如果在Load语句后加上c.newInstance(); 就会有Hi输出,对该类进行实例化时才初始化类对象。
如果换成Class.forName("cn.fengd.Dummy");或者new Dummy()呢?
都会输出Hi。
转自http://blog.csdn.net/radic_feng/article/details/6897898
关键字:Java,类加载器(Class Loader)
主要从如下几个部分进行介绍:
类加载的过程
类加载器的层级
类加载器的操作原则
为什么要个性化类加载器
其它相关问题
类加载的过程
类加载器的主要工作就是把类文件加载到JVM中。如下图所示,其过程分为三步:
加载:定位要加载的类文件,并将其字节流装载到JVM中;
链接:给要加载的类分配最基本的内存结构保存其信息,比如属性,方法以及引用的类。在该阶段,该类还处于不可用状态;
验证:对加载的字节流进行验证,比如格式上的,安全方面的;
内存分配:为该类准备内存空间来表示其属性,方法以及引用的类;
解析:加载该类所引用的其它类,比如父类,实现的接口等。
初始化:对类变量进行赋值。

类加载器的层级
下图虚线以上是JDK提供的几个重要的类加载器,详细说明如下:
Bootstrap Class Loader: 当启动包含主函数的类时,加载JAVA_HOME/lib目录下或-Xbootclasspath指定目录的jar包;
Extention Class Loader:加载JAVA_HOME/lib/ext目录下的或-Djava.ext.dirs指定目录下的jar包。
System Class Loader:加载classpath或者-Djava.class.path指定目录下的类或jar包。

需要了解的是:
除了Bootstrap Class Loader外,其它的类加载器都是java.lang.ClassLoader类的子类;
Bootstrap Class Loader不是用Java实现,如果你没有使用个性化类加载器,那么java.lang.String.class.getClassLoader()就为null,Extension Class Loader的父加载器也为null;
获得类加载器的几种方式:
获得Bootstrap Class Loader:试图获得Bootstrap Class Loader,得到的必然是null。可以用如下方式验证下:使用rt.jar包内的类对象的getClassLoader方法,比如java.lang.String.class.getClassLoader()可以得到或者获得Extention Class Loader,再调用getParent方法获得;
获得Extention Class Loader:使用JAVA_HOME/lib/ext目录下jar包内的类对象的getClassLoader方法或者先获得System Class Loader,再通过它的getParent方法获得;
获得System Class Loader:调用包含主函数的类对象的getClassLoader方法或者在主函数内调用Thread.currentThread().getContextClassLoader()或者调用ClassLoader.getSystemClassLoader();
获得User-Defined Class Loader:调用类对象的getClassLoader方法或者调用Thread.currentThread().getContextClassLoader();
类加载器的操作原则
代理原则
可见性原则
唯一性原则
代理原则
代理原则指的是一个类加载器在加载一个类时会请求它的父加载器代理加载,父加载器也会请求它的父加载器代理加载,如下图所示。

为什么要使用代理模式呢?首先这样可以减少重复的加载一个类。(还有其它原因吗?)
容易误解的地方:
一般会以为类加载器的代理顺序是Parent First的,也就是:
加载一个类时,类加载器首先检查自己是否已经加载了该类,如果已加载,则返回;否则请父加载器代理;
父加载器循环重复1的操作一直到Bootstrap Class Loader;
如果Bootstrap Class Loader也没有加载该类,将尝试进行加载,加载成功则返回;如果失败,抛出ClassNotFoundException,则由子加载器进行加载;
子类加载器捕捉异常后尝试加载,如果成功则返回,如果失败则抛出ClassNotFoundException,直到发起加载的子类加载器。
这种理解对Bootstrap Class Loader,Extention Class Loader,System Class Loader这些加载器是正确的,但一些个性化的加载器则不然,比如,IBM Web Sphere Portal Server实现的一些类加载器就是Parent Last的,是子加载器首先尝试加载,如果加载失败才会请父加载器,这样做的原因是:假如你期望某个版本log4j被所有应用使用,就把它放在WAS_HOME的库里,WAS启动时会加载它。如果某个应用想使用另外一个版本的log4j,如果使用Parent First,这是无法实现的,因为父加载器里已经加载了log4j内的类。但如果使用Parent Last,负责加载应用的类加载器会优先加载另外一个版本的log4j。(更多信息)
可见性原则
每个类对类加载器的可见性是不一样的,如下图所示。
扩展知识,OSGi就是利用这个特点,每一个bundle由一个单独的类加载器加载,因此每个类加载器都可以加载某个类的一个版本,因此整个系统就可以使用一个类的多个版本。
唯一性原则
每一个类在一个加载器里最多加载一次。
扩展知识1:准确地讲,Singleton模式所指的单例指的是在一组类加载器中某个类的对象只有一份。
扩展知识2:一个类可以被多个类加载器加载,每个类对象在各自的namespace内,对类对象进行比较或者对实例进行类型转换时,会同时比较各自的名字空间,比如:
Klass类被ClassLoaderA加载,假设类对象为KlassA;同时被ClassLoaderB加载,假设类对象为KlassB,那么KlassA不等于KlassB。同时ClassA的实例被cast成KlassB时会抛出ClassCastException异常。
为什么要个性化类加载器
个性化类加载器给Java语言增加了很多灵活性,主要的用途有:
可以从多个地方加载类,比如网络上,数据库中,甚至即时的编译源文件获得类文件;
个性化后类加载器可以在运行时原则性的加载某个版本的类文件;
个性化后类加载器可以动态卸载一些类;
个性化后类加载器可以对类进行解密解压缩后再载入类。
其它相关问题
类的隐式和显式加载
隐式加载:当一个类被引用,被继承或者被实例化时会被隐式加载,如果加载失败,是抛出NoClassDefFoundError。
显式加载:使用如下方法,如果加载失败会抛出ClassNotFoundException。
cl.loadClass(),cl是类加载器的一个实例;
Class.forName(),使用当前类的类加载器进行加载。
类的静态块的执行
假如有如下类:
[java] view plaincopy
package cn.fengd;
public class Dummy {
static {
System.out.println("Hi");
}
}
另建一个测试类:
[java] view plaincopy
package cn.fengd;
public class ClassLoaderTest {
public static void main(String[] args) throws InstantiationException, Exception {
try {
/*
* Different ways of loading.
*/
Class c = ClassLoaderTest.class.getClassLoader().loadClass("cn.fengd.Dummy");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
运行后效果如何呢?
不会输出Hi。由此可见使用loadClass后Class类对象并没有初始化;
如果在Load语句后加上c.newInstance(); 就会有Hi输出,对该类进行实例化时才初始化类对象。
如果换一种加载语句Class c = Class.forName("cn.fengd.Dummy", false, ClassLoader.getSystemClassLoader());
不会输出Hi。因为参数false表示不需要初始化该类对象;
如果在Load语句后加上c.newInstance(); 就会有Hi输出,对该类进行实例化时才初始化类对象。
如果换成Class.forName("cn.fengd.Dummy");或者new Dummy()呢?
都会输出Hi。
转自http://blog.csdn.net/radic_feng/article/details/6897898
发表评论
文章已被作者锁定,不允许评论。
-
ReentrantLock与Condition
2017-03-17 14:25 535多线程和并发性并不是什么新内容,但是 Java 语言设计中的创 ... -
java linux监控
2017-03-13 17:49 514http://agapple.iteye.com/blog/1 ... -
transient和volatile两个关键字
2017-02-16 09:47 581transient和volatile两个关 ... -
java 锁机制
2016-12-09 13:43 483一段synchronized的代码被 ... -
java 正则表达式
2016-12-02 10:28 526众所周知,在程序开发中,难免会遇到需要匹配、查找、替换、判断字 ... -
java ClassNotFoundException和NoClassDefFoundException的差别
2016-08-17 19:47 923首先从名字上可以看出一类是异常,一类属于错误。异常可以通过异常 ... -
ThreadLocal
2016-07-19 11:10 348ThreadLocal是什么 Thre ... -
java CAS
2016-07-10 14:55 360cas 乐观锁每次不锁定整个线程,在操作之前进行判断。悲观锁独 ... -
concurrenthashmap
2016-07-10 11:11 431hash table虽然性能上不如 ... -
java 线程池的使用
2016-07-10 09:52 3821. 引言 合理利用线程池能够带来三个好处。第一:降低资源消 ... -
java.util.concurrent
2016-07-03 16:24 417我们都知道,在JDK1.5之 ... -
JVM 配置 以及垃圾收集器的选择
2016-04-15 12:36 742JVM监控的关键指标说明: a) FGC的环比增加次数。Zab ... -
jvm实时监控工具
2016-04-09 09:35 491 -
哈希 、一致性哈希、余数式哈希
2016-04-07 16:10 879什么是Hash Hash,一 ... -
jvm dump 相关
2016-03-22 17:22 687http://www.cnblogs.com/edwardla ... -
深入剖析volatile关键字
2016-03-21 16:02 565深入剖析volatile关键字 ... -
java线程安全问题之静态变量、实例变量、局部变量
2016-03-08 12:52 583java多线程编程中,存在很多线程安全问题,至于什么是线程安全 ... -
有状态的bean和无状态的bean的区别
2016-03-08 11:23 1553有状态会话bean :每个用户有自己特有的一个实例,在用户的生 ... -
Java nio详解
2016-01-20 16:30 569http://www.ibm.com/developerwor ... -
java 不定长数组
2015-11-24 15:00 810在调用某个方法时,若是方法的参数个数事先无法确定该如何处理 ...
相关推荐
在Java中,类加载器(Class Loader)是一项核心机制,用于将字节码(.class文件)加载到JVM中,使其成为运行时的对象。类加载器不仅实现了类的加载功能,还确保了Java程序在多线程环境中的安全性和隔离性。类加载器...
### Java类加载器详解 Java类加载器是Java运行时环境的一个关键组成部分,负责将类文件(.class)从各种来源加载到JVM中。它不仅管理类的生命周期,还确保了类的正确加载和初始化,是Java动态特性的基石。 #### 类...
在Java编程中,类加载器(Class Loader)扮演着至关重要的角色,它负责查找并加载Java类到JVM内存中。本部分我们将深入探讨JVM中的类加载器,特别是根类加载器、扩展类加载器和系统类加载器。 首先,让我们了解类...
1. **引导类加载器(Bootstrap Class Loader)**:这是系统级的类加载器,它负责加载 Java 核心类库,如 `java.lang.Object`。由于这是一个本地实现,开发者无法直接获取其实例。 示例: ```java System.out....
可以通过调用`Class`对象的`getClassLoader()`方法来获取当前类的类加载器,示例代码如下: ```java public class HelloWorld { public static void main(String[] args) { HelloWorld hello = new HelloWorld();...
### Java自定义类加载器(Class Loader)详解 #### 一、引言 在Java语言中,类加载机制是其动态特性的核心之一。通过类加载器(Class Loader),Java程序能够在运行时根据需要加载所需的类,从而实现高度的灵活性...
总结来说,Java配置加载机制的关键在于抽象出加载器和提供者接口,利用泛型处理不同类型的配置数据,通过工厂模式动态创建合适的配置加载器和提供者,以实现配置的灵活加载和管理。这种设计对于大型、复杂的应用程序...
* User-Defined Class Loader:这是开发人员通过拓展 ClassLoader 类定义的自定义加载器,加载程序员定义的一些类。 类加载器之间存在一种委派模式(Delegation Mode),当 JVM 加载一个类的时候,下层的加载器会将...
在 Java 语言中,类加载器(ClassLoader)是 Java 运行时环境的核心组成部分之一,它负责将编译后的 `.class` 文件加载到 JVM 中执行。从 JDK 1.0 开始,随着 Java Applet 的出现以及网络应用的需求增加,类加载机制...
在Java编程中,类加载器(Class Loader)扮演着至关重要的角色。它不仅负责将类的二进制字节流加载到内存中,还参与了类的实例化过程。本文将深入探讨三种核心的类加载器:启动类加载器(Bootstrap ClassLoader)、...
Bootstrap Class Loader是所有类加载器的父亲,负责加载基本的Java API,包括Object类。Extension Class Loader加载出了基本API之外的一些拓展类,包括一些与安全性能相关的类。System Class Loader加载应用程序中的...
Bootstrap Class Loader是最基础的类加载器,由C/C++实现,负责加载Java核心API,包括Object类。Extension Class Loader加载扩展类,涉及安全和性能相关的类。System Class Loader(也称为App Class Loader)则加载...
1. **引导类加载器(Bootstrap Class Loader)**:由C/C++实现,负责加载Java核心库,如`rt.jar`中的类,包括基础类`java.lang.Object`。 2. **扩展类加载器(Extension Class Loader)**:加载JDK的扩展库,通常...
**EJB类加载器详解** 在Java企业版(Java EE)中,Enterprise JavaBeans (EJB) 是一种核心组件,用于构建可扩展、安全且事务处理能力强大的分布式应用程序。类加载器是Java虚拟机(JVM)的一个重要组成部分,负责...
双亲委派机制是Java设计类加载器的一种策略,其核心思想是:子类加载器优先将加载请求委托给父类加载器,只有当父类加载器无法加载时,子类加载器才会尝试自己加载。这种机制有助于维护Java程序的稳定性,避免重复...
通常情况下,`Class.forName` 使用当前线程的上下文类加载器,但如果提供了第三个参数,则使用指定的类加载器。 **示例**: ```java try { ClassLoader myLoader = new MyClassLoader(); Class<?> clazz = Class....
装载过程是由类加载器(Class Loader)完成的,类加载器分为应用程序类加载器(Application Class Loader)、扩展类加载器(Extension Class Loader)和引导类加载器(Bootstrap Class Loader)。每个类加载器都有特定的任务...
7. **类加载器(Class Loader)** - 类加载器负责读取字节码文件(.class),解析出构成类或接口的数据,并生成代表类或接口的`java.lang.Class`对象。 8. **更快的类加载(Faster Class Loading)** - 快速类加载是指...