1:what is  it

jvm把描述类的数据从class字节码文件加载到内存,并对数据进行校验、解析、初始化,最终成为jvm直接使用的数据类型

1、ClassNotFoundExcetpion 
  我们在开发中,经常可以遇见java.lang.ClassNotFoundExcetpion这个异常,今天我就来总结一下这个问题。对于这个异常,它实质涉及到了java技术体系中的类加载。Java的类加载机制是技术体系中比较核心的部分,虽然它和我们直接打交道不多,但是对其背后的机理有一定理解有助于我们排查程序中出现的类加载失败等技术问题。 
2、类的加载过程 
  一个java文件从被加载到被卸载这个生命过程,总共要经历5个阶段,JVM将类加载过程分为: 
  加载->链接(验证+准备+解析)->初始化(使用前的准备)->使用->卸载 
(1)加载 
  首先通过一个类的全限定名来获取此类的二进制字节流;其次将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构;最后在java堆中生成一个代表这个类的Class对象,作为方法区这些数据的访问入口。总的来说就是查找并加载类的二进制数据。 
(2)链接: 
  验证:确保被加载类的正确性; 
  准备:为类的静态变量分配内存,并将其初始化为默认值; 
  解析:把类中的符号引用转换为直接引用; 
(3)为类的静态变量赋予正确的初始值 
3、类的初始化 
(1)类什么时候才被初始化 
  1)创建类的实例,也就是new一个对象 
  2)访问某个类或接口的静态变量,或者对该静态变量赋值 
  3)调用类的静态方法 
  4)反射(Class.forName(“com.lyj.load”)) 
  5)初始化一个类的子类(会首先初始化子类的父类) 
  6)JVM启动时标明的启动类,即文件名和类名相同的那个类 
(2)类的初始化顺序 
  1)如果这个类还没有被加载和链接,那先进行加载和链接 
  2)假如这个类存在直接父类,并且这个类还没有被初始化(注意:在一个类加载器中,类只能初始化一次),那就初始化直接的父类(不适用于接口) 
  3)加入类中存在初始化语句(如static变量和static块),那就依次执行这些初始化语句。 
  4)总的来说,初始化顺序依次是:(静态变量、静态初始化块)–>(变量、初始化块)–> 构造器;如果有父类,则顺序是:父类static方法 –> 子类static方法 –> 父类构造方法- -> 子类构造方法

2:when it happens

初始化时机  Jvm规范规定了 有且仅有5种

1new getstatic putstatic invokestatic 4条指令时 (new一个对象 使用静态属性和方法)

2使用java.lang.reflect包的方法对某个类进行反射调用 forname

3 初始化一个类时发现其父类尚未初始化 先初始化父类

4Jvm启动时  加载main方法所在的类

5Jdk1.7的动态语言 MathodHander解析结果为 REF_getStatic REF_putStatic REF_inpokStatic 加载对应类

3:ClassLoader

类加载器 实现类加载的动作的类

3.1jvm中的类加载器:

1.引导类(Bootstrap classloader):组成Java平台的类,包括jre/lib/rt.jar -Xbootclasspath指定  按文件名识别

2.扩展类(Extensions classloader):使用Java扩展机制的类,jre/lib/ext  java.ext.dirs指定的其他jar

3.用户类(application classloader 系统、应用类加载器):

由开发者定义的类和没有利用扩展机制的第三方类,这些类的位置由用户指定,

一般通过使用-classpath命令行选项或者使用CLASSPATH环境变量来指定其位置。

 3.2层次结构:

Bootstrap > Extension > Application > user classloder

但他们之间不是以继承的关系来实现 而是组合的形式来复用父加载器的方法

每个累加器都有parent属性指向它的上级类加器(见下面代码)

3.3双亲委派模型(见下图)

若一个类加载器收到类加载的请求 他首先自己不会尝试加载这个类 而是把这个请求委托给父类加器去完成

每一个类加器都如此 这样所有的类加载请求都会传入到bootstrap中 只有当父加载器加载不了时

此时父加载器抛出异常,子加载器捕获后 再在自己的领域内尝试加载

比如:

* 代码中出现了这么一行:new A();

> 系统发现了自己加载的类,其中包含了new A(),这说明需要系统去加载A类

> 系统会给自己的领导打电话:让扩展去自己的地盘去加载A类

> 扩展会给自己的领导打电话:让引导去自己的地盘去加载A类

> 引导自己真的去rt.jar中寻找A类

* 如果找到了,那么加载之,然后返回A对应的Class对象给扩展,扩展也会它这个Class返回给系统,结束了!

* 如果没找到:

> 引导给扩展返回了一个null,扩展会自己去自己的地盘,去寻找A类

* 如果找到了,那么加载之,然后返回A对应的Class对象给系统,结束了!

* 如果没找到

> 扩展返回一个null给系统了,系统去自己的地盘(应用程序下)加载A类

* 如果找到了,那么加载之,然后返回这个Class,结束了!
                  * 如果没找到,抛出异常ClassNotFoundException

好处:java类随着他的类加载器一起具备了一种带有优先级的层次关系 如java.lang,Object类放在rt.jar内

无论哪一个类加载器要加载它,最终都要委托到启动类加载器加载他 因此 在各种类加载器环境中使用Object都是同一个类

如果没有这种委派机制 用 户自定义一个java.lang.Object放在classpath下

加载时系统将会出现多个不同的object类 Java类型体系的最基础的行为就会被破坏掉 程序也将一片混乱

同一个类: 只有2个类由同一个类加载器加载的前提下 他们才可能相等

即 相等条件: 相同的class文件 + 同一个类加载器加载

 3.5core code

1     // The parent class loader for delegation
2     private ClassLoader parent;

 1     protected synchronized Class<?> loadClass(String name, boolean resolve)
 2
 3     throws ClassNotFoundException
 4
 5     {
 6
 7     // First, check if the class has already been loaded
 8
 9     Class c = findLoadedClass(name);
10
11     if (c == null) {
12
13         try {
14
15         if (parent != null) {
16
17             c = parent.loadClass(name, false);
18
19         } else {
20
21             c = findBootstrapClassOrNull(name);//只有bootstrap会执行这一句
22
23         }
24
25         } catch (ClassNotFoundException e) {
26
27                 // ClassNotFoundException thrown if class not found
28
29                 // from the non-null parent class loader
30
31         }
32
33             if (c == null) {
34
35             // If still not found, then invoke findClass in order
36
37             // to find the class.
38
39             c = findClass(name);
40
41         }
42
43     }
44
45     if (resolve) {
46
47         resolveClass(c);
48
49     }
50
51     return c;
52
53 }

先判断是否已经加载 内存中已有 不再加载

不存在 判断parent加载器是否为null   no 则调用父加载器的loadClass方法 否则

该类加载器为bootstrap 他执行自己的加载方法findBootstrapClassOrNull

当父类在自己的领域内找不到时 findClass会抛出异常

子类捕获异常后 就会尝试在自己领域内加载

3.6图解

3.7自定义类加载器

我们也可以通过继承ClassLoader类来完成自定义类加载器,自类加载器的目的一般是为了加载网络上的类,因为这会让class在网络中传输,为了安全,那么class一定是需要加密的,所以需要自定义的类加载器来加载(自定义的类加载器需要做解密工作)。

ClassLoader加载类都是通过loadClass()方法来完成的,loadClass()方法的工作流程如下:

l  调用findLoadedClass ()方法查看该类是否已经被加载过了,如果该没有加载过,那么这个方法返回null;

l  判断findLoadedClass()方法返回的是否为null,如果不是null那么直接返回,这可以避免同一个类被加载两次;

l  如果findLoadedClass()返回的是null,那么就启动代理模式(委托机制),即调用上级的loadClass()方法,获取上级的方法是getParent(),当然上级可能还有上级,这个动作就一直向上走;

l  如果getParent().loadClass()返回的不是null,这说明上级加载成功了,那么就加载结果;

l  如果上级返回的是null,这说明需要自己出手了,这时loadClass()方法会调用本类的findClass()方法来加载类;

l  这说明我们只需要重写ClassLoader的findClass()方法,这就可以了!如果重写了loadClass()方法覆盖了代理模式!

OK,通过上面的分析,我们知道要自定义一个类加载器,只需要继承ClassLoader类,然后重写它的findClass()方法即可。那么在findClass()方法中我们要完成哪些工作呢?

l  找到class文件,把它加载到一个byte[]中;

l  调用defineClass()方法,把byte[]传递给这个方法即可。

 1 public class FileSystemClassLoader extends ClassLoader {
 2     private String classpath ;
 3
 4     public FileSystemClassLoader() {}
 5
 6     public FileSystemClassLoader (String classpath) {
 7         this.classpath = classpath;
 8     }
 9
10     @Override
11     public Class<?> findClass(String name) throws ClassNotFoundException {
12         try {
13             byte[] datas = getClassData(name);
14             if(datas  == null) {
15                 throw new ClassNotFoundException("类没有找到:" + name);
16             }
17             return this.defineClass (name, datas, 0, datas.length);
18         } catch (IOException e) {
19             e.printStackTrace();
20             throw new ClassNotFoundException("类找不到:" + name);
21         }
22     }
23
24     private byte[] getClassData(String name) throws IOException {
25         name = name.replace(".", "\\") + ".class";
26         File classFile = new File(classpath, name);
27         return FileUtils .readFileToByteArray(classFile);
28     }
29 }
30
31
32
33         ClassLoader loader = new FileSystemClassLoader("F:\\classpath");
34         Class clazz = loader.loadClass("cn.itcast.utils.CommonUtils");
35         Method method = clazz.getMethod("md5", String.class);
36         String result = (String) method.invoke(null, "qdmmy6");
37         System.out.println(result);

4tomcat类加器 Tomcat 5

Bootstrap>Extension>Application>Common >shared>webappX>jsperLoader

> Catalina

/common:tomcat和所有webapp共同使用

/server:tomcat使用 webapp不可见

/shared:所以webapp共同使用 tomcat不能用

•Common:该类加载器包含一些对Tomcat内部类和web应用可见的额外类。

其中包括(1)jasper-compiler.jar:JSP 2.0编译器(2)jsp-api.jar:JSP 2.0 API(3)servlet-api.jar:servlet 2.4 API等等。对应文件夹  /common

•Catalina:该加载器初始化用来包含实现Tomcat 5本身所需要所有类和资源;对应文件夹 /server

•Shared:在所有的web应用程序间共享的类和资源;对应文件夹  /shared

•WebappX:为每个部署在单个Tomcat 5实例上的Web应用创建的类加载器。

加载/WEB-INF/classes和WEB-INF/lib下的类和资源。

值得注意的是,Web应用程序类加载器行为与默认的Java 2委派模型不同。当一个加载类的请求被WebappX类加载器处理时,类加载器将首先查看本地库,而非在查看前就委派,

但是也有例外,作为JRE基本类一部分的类不能被覆盖,但是对与一些类,可以使用J2SE 1.4的Endorsed Standards Override机制。最后,任何包含servlet API的JAR包都将被该类加载器忽略。

5Tomcat 6.0:

Bootstrap>Extension>Application>Common > webappX

在tomcat中类的加载稍有不同,如下图:

当tomcat启动时,会创建几种类加载器:

1 Bootstrap 引导类加载器

加载JVM启动所需的类,以及标准扩展类(位于jre/lib/ext下)

2 System 系统类加载器

加载tomcat启动的类,比如bootstrap.jar,通常在catalina.bat或者catalina.sh中指定。

位于CATALINA_HOME/bin下。

3 Common 通用类加载器

加载tomcat使用以及应用通用的一些类,位于CATALINA_HOME/lib下,比如servlet-api.jar

4 webapp 应用类加载器

每个应用在部署后,都会创建一个唯一的类加载器。

该类加载器会加载位于 WEB-INF/lib下的jar文件中的class 和 WEB-INF/classes下的class文件。

当应用需要到某个类时,则会按照下面的顺序进行类加载:

  1 使用bootstrap引导类加载器加载

  2 使用system系统类加载器加载

  3 使用应用类加载器在WEB-INF/classes中加载

  4 使用应用类加载器在WEB-INF/lib中加载

  5 使用common类加载器在CATALINA_HOME/lib中加载

问题扩展

通过对上面tomcat类加载机制的理解,就不难明白 为什么java文件放在Eclipse中的src文件夹下会优先jar包中的class?

这是因为Eclipse中的src文件夹中的文件java以及webContent中的JSP都会在tomcat启动时,

被编译成class文件放在 WEB-INF/class 中。

而Eclipse外部引用的jar包,则相当于放在 WEB-INF/lib 中。

因此肯定是 java文件或者JSP文件编译出的class优先加载。

通过这样,我们就可以简单的把java文件放置在src文件夹中,通过对该java文件的修改以及调试,

便于学习拥有源码java文件、却没有打包成xxx-source的jar包。

另外呢,开发者也会因为粗心而犯下面的错误。

在 CATALINA_HOME/lib 以及 WEB-INF/lib 中放置了 不同版本的jar包,此时就会导致某些情况下报加载不到类的错误。

还有如果多个应用使用同一jar包文件,当放置了多份,就可能导致 多个应用间 出现类加载不到的错误。

转载于:https://www.cnblogs.com/wihainan/p/4757245.html

java class load 类加载相关推荐

  1. Java单元测试和类加载

    Java单元测试和类加载 回顾: 1 Lambda表达式:相等于匿名内部类,实现代码作为方法的参数传统.函数式接口 变量=(参数列表)->{方法体};注意: ->操作符 分成两部分 左侧: ...

  2. Java反射 - 动态类加载和重载

    可以使用Java在运行时加载和重新加载类,虽然它不像人们希望的那样简单. 本文将解释何时以及如何在Java中加载和重新加载类. ClassLoader Java应用程序中的所有类都使用java.lan ...

  3. 用Java Instrumentation 在类加载时添加记录

    用Java Instrumentation 在类加载时添加记录 发布者:xanadu0214   来源:网络转载   发布日期:2013年11月06日   Java学习交流群:471651004 在分 ...

  4. Java基础:类加载器

    系列阅读 Java基础:类加载器 Java基础:反射 Java基础:注解 Java基础:动态代理 1. 什么是类加载器 类加载器就是用来加载类的东西!类加载器也是一个类:ClassLoader 类加载 ...

  5. java中的类加载器有,Java自定义的类加载器,java自定义加载,在java中类加载器有以...

    Java自定义的类加载器,java自定义加载,在java中类加载器有以 在java中类加载器有以下几种java虚拟机自带的加载器 1)根类加载器(Bootstrap,c++实现)2)扩展类加载器(Ex ...

  6. 一次性搞清Java中的类加载问题

    摘要:很多时候提到类加载,大家总是没法马上回忆起顺序,这篇文章会用一个例子为你把类加载的诸多问题一次性澄清. 本文分享自华为云社区<用1个例子加5个问题,一次性搞清java中的类加载问题[奔跑吧 ...

  7. Java虚拟机中类加载机制详解

    Java虚拟机中类加载机制详解 1,什么是java类加载机制 **首先在java中,是通过编译来生成.class文件(可能在本地,或者网页下载),java的类加载机制就是 将这些.class文件加载到 ...

  8. Java和SpringBoot类加载机制

    文章目录 一.SPI介绍 1. SPI机制 2. SPI使用案例 二.Java类加载机制 1.双亲委派模型 2. 双亲委派模型缺陷 3. 使用线程上下文类加载器(ContextClassLoader) ...

  9. 深入Java虚拟机之类加载

    深入Java虚拟机_ClassLoader 类加载器深入剖析 Java虚拟机与程序的生命周期 在如下几种情况下,Java虚拟机将结束生命周期 执行了System.exit()方法 程序正常执行结束 程 ...

最新文章

  1. 数论计算机科学与技术专业就业前景好,【数学】数学专业就业前景:你看不见的“前途似锦”...
  2. android 获取布局textview,android – 获取TextView中文本的位置
  3. 线性代数:第三章 矩阵的初等变换与线性方程组(2)线性方程组的解 初等方阵
  4. 算法入门开灯问题,新做法
  5. 另一种办法直接在宿主机上的文件夹内查看Docker镜像运行的日志文件
  6. 入门人工智能,我究竟该学些什么?
  7. 【Flink】解决Flink在测试环境无法保存checkpoint问题
  8. java类的主体是_java入门知识
  9. Java、SQL 十年混战史:Oracle 告 Google、“窃”IBM 往事
  10. 如何让主机合规分析报告评分达到90分?
  11. 第4周第4课:gzip、bzip2、xz
  12. 大数据Hadoop学习文章汇总
  13. selenium+phantomjs截长图踩坑
  14. Top-down Visual Saliency Guided by Captions
  15. 从我的角度体会平安车险的服务
  16. 2000-XP-2003操作系统常见问题
  17. 程序员都应该知道的福利
  18. Windows cmd命令 个人常用0529
  19. txt文件导入到Excel
  20. 看不懂CAD建筑图纸怎么办?有什么CAD快速看图或识图的技巧吗?

热门文章

  1. eureka需要替换吗_nacos无缝替换eureka
  2. 面试准备-Shell脚本
  3. Windows安装zookeeper 单机版
  4. 七、线性表的链式存储结构
  5. LeetCode 258 Add Digits
  6. Spring 4 官方文档学习(十)数据访问之JDBC
  7. 结对项目开发-电梯调度
  8. jQuery学习教程(一):入门
  9. 第8章-常用优先级和css3
  10. oracle安装,未找到文件 F:\app\Administrator\product\11.2.0\dbhome_2\owb\external\oc4j_ap