java class load 类加载

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/431443.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python回溯方法的模板_Python基于回溯法子集树模板解决0-1背包问题实例

本文实例讲述了Python基于回溯法子集树模板解决0-1背包问题。分享给大家供大家参考&#xff0c;具体如下&#xff1a;问题给定N个物品和一个背包。物品i的重量是Wi,其价值位Vi &#xff0c;背包的容量为C。问应该如何选择装入背包的物品&#xff0c;使得放入背包的物品的总价值…

debian执行php网页,如何在Debian上安装和使用PHP Composer

php composer是一个包管理工具&#xff0c;它消除了手动维护应用程序的PHP包的麻烦&#xff0c;可以使用composer轻松安装所有必需的包。本篇文章将介绍在Debian系统上安装和配置PHP Composer的方法。1、条件shell使用sudo权限访问正在运行的debian系统。必须安装和配置5.3或更…

python图灵测试_最前沿:基于GAN和RL的思想来训练对话生成,通过图灵测试可期!...

PS&#xff1a;本文分析略深&#xff0c;需要一定的RL和GAN的基础。前两天&#xff0c;Stanford的NLP小组出了一篇神经网络对话生成的论文&#xff1a;标题就是使用对抗学习来做神经对话生成。这个idea非常的赞&#xff01;在我看来是通往图灵测试的正确一步。以前的对话生成&a…

java map 实现 序列化,MapReduce序列化

序列化就是把内存中的对象转换成字节序列以便于存储到磁盘(持久化)和网络传输。反序列化就是将字节序列或者是持久化的数据转换成内存中的对象。内存中的对象只能本地进程使用&#xff0c;断掉后就消失了&#xff0c;也不能被发送到网络上的另一台机器&#xff0c;序列化可以将…

安装php时,make步骤报错make: *** [sapi/fpm/php-fpm] Error 1

安装PHP过程中&#xff0c;make步骤报错&#xff1a;(集中网络上各种解决方法) (1)-liconv -o sapi/fpm/php-fpm /usr/bin/ld: cannot find -liconv collect2: ld returned 1 exit status make: *** [sapi/fpm/php-fpm] Error 1 [rootlocalhost php-5.4.5]# 初步定位是iconv的问…

JAVA_if或者怎么用,Java If语句

Java教程 - Java If语句Java if语句用于根据条件执行一个代码块。Java If语句下面是Java if语句的最简单形式:if(condition)statement;condition是一个布尔表达式。如果condition是true那么执行语句。如果condition是false&#xff0c;那么绕过语句。以下代码根据an的值输出消息…

iOS开发——高级技术本地化与国际化详解

本地化与国际化详解效果如下&#xff1a;英语&#xff1a; 中文&#xff1a; 具体实现如下&#xff1a;一。先做准备&#xff1a;设置程序本地化在xcode点击程序图标…

hog特征提取python代码_hog特征提取-python实现

【转载自 https://blog.csdn.net/ppp8300885/article/details/71078555】全部代码&#xff1a;importcv2importnumpy as npimportmathimportmatplotlib.pyplot as pltclassHog_descriptor():def __init__(self, img, cell_size16, bin_size8):self.imgimgself.img np.sqrt(img …

matlab 矢量化,matlab矢量化编程简要

一、基本技术1)MATLAB索引或引用(MATLAB Indexing or Referencing)在MATLAB中有三种基本方法可以选取一个矩阵的子阵。它们分别是下标法&#xff0c;线性法和逻辑法(subscripted, linear,andlogical)。1.1)下标法非常简单&#xff0c;看几个例子就好。A 6:12;A([3,5])ans 8 10…

Hadoop-HBASE案例分析-Hadoop学习笔记二

之前有幸在MOOC学院抽中小象学院hadoop体验课。 这是小象学院hadoop2.X概述第八章的笔记 主要介绍HBase&#xff0c;一个分布式数据库的应用案例。 案例概况&#xff1a; 1&#xff09;时间序列数据库(OpenTSDB) 用HBase储存时间序列数据&#xff0c;每时每刻都在解决&#xff…

python测试udp端口_怎样测试UDP端口

文章概述&#xff1a;怎样测试远程UDP端口&#xff0c;我们一般情况下&#xff0c;应用服务都使用的TCP端口&#xff0c;但是某些情况下&#xff0c;我们也需要开启UDP端口。本文简要描述怎样测试UDP端口是否正常&#xff1f;TCP端口大家都知道,比如80端口&#xff0c;可以使用…

Matlab经纬度坐标转换xy坐标,经纬度坐标系转换为UTM坐标系(matlab)

如题所说&#xff0c;直接上程序。验证自己做一下&#xff0c;结果应该是对的。诚不我欺。(注意&#xff1a;程序名与函数名保持一致&#xff01;&#xff01;&#xff01;)function[shuchu]lat_lon2utm(lat_shuru,lon_shuru)%地理经纬度坐标转换为UTM坐标size_shuzusize(lat_s…

ASP.NET MVC中的模型装配 封装方法 非常好用

下面说一下我们知道在asp.net mvc中 视图可以绑定一个实体模型 然后我们三层架构中也有一个model模型 但是这两个很多时候却是不一样的对象来的 就拿微软的官方mvc例子来说明 微软的视图实体中 有loginmodel 有registermodel 等等 这些视图模型 都只是占用户实体的某几个字段而…

python webviewer爬虫_爬虫再也不怕检测浏览器环境了

背景之前爬虫 驱动个 selenium 基本上就可以了.但是现在各种检测浏览器环境...特别是不熟悉 js 的同学就更烦了本文是直接把 selenium pyppeteer 以及正常打开浏览器 的环境差异直接列出来这样你就可以更愉快的爬虫了(可以直接把环境全部模拟上,或者大概看看有啥,下次看人家混淆…

jQuery: 整理2---操作元素的样式

1.获取元素的class属性值 attr("class") <div id"conBlue" class"blue larger">天蓝色</div>const cla $("#conBlue").attr("class") console.log(cla) // blue larger 2.设置元素的样式 attr("class…

matlab空间曲面拟合,matlab如何进行曲面拟合

matlab如何进行曲面拟合以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;在一丘陵地带测量高程&#xff0c;x和 y方向每隔100米测一个点&#xff0c;得高程如下表&#xff0c;试插值一曲面&am…

python做的游戏可以导出吗_Python for RenderDoc批量导出模型和贴图

故事背景&#xff1a;美术那里有需求&#xff0c;需要别人游戏的模型&#xff0c;来借鉴一下&#xff0c;问我是否有工具可以一键导出模型。我就搜索了一下RenderDoc批量导出图片&#xff0c;结果搜到了用C改RenderDoc源码的文章。让RenderDoc批量导出纹理_专栏-CSDN博客​blog…

treegrid.bootstrap使用说明

treegrid.bootstrap使用说明 这个插件是用来做有层次的表格的&#xff0c;大概如图&#xff1a; 官网 http://maxazan.github.io/jquery-treegrid/ 使用这个控件之前需要引入以下css及js&#xff08;因为用到了 bootstrap.js 所以加上了 bootstrap的样式和脚本&#xff09; bo…

matlab 条形图横坐标,Matlab条形图bar横坐标间距设置

1. 默认横坐标数据 X[x1, x2, x3, x4, x5, x6] %一行六列bar(X); %绘制基础条形图2. 修改横坐标标签#考虑横坐标标签文本较长且字体较大的情况bar(X);set(gca, xticklabels,{Apple, Orange, Banana, Pear, Pitaya, Lemon}, Fontname, Times New Roman, Fontsize, 16); %修…

成都python数据分析师职业技能_合格大数据分析师应该具备的技能

课程七、建模分析师之软技能 - 数据库技术本部分课程主要介绍MySQL数据库的安装使用及常用数据操作1、关系型数据库介绍2、MySQL的基本操作:1)数据库的操作2)数据表的操作3)备份与恢复3、常用的SQL语句:1)查询语句(SELECT)2)插入语句(INSERT)3)更新语句(UPDATE)4)删除语句(DELE…