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,一经查实,立即删除!

相关文章

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

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

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点击程序图标…

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

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

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

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

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); %修…

stm32 485和232可以用同一个串口吗_STM32的复用时钟何时开启?

STM32的AFIO时钟真的是在开启引脚复用功能的时候开启吗&#xff1f;其实并不是~什么是复用&#xff1f;我们知道&#xff0c;STM32有很多外设&#xff0c;这些外设的外部引脚都是与GPIO共用的。我们可以通过软件来配置引脚作为GPIO引脚还是作为外设引脚。当引脚配置为外设引脚时…

php 简析对象,PHP白盒审计工具RIPS源码简析

RIPS是一款对PHP源码进行风险扫描的工具&#xff0c;其对代码扫描的方式是常规的正则匹配&#xff0c;确定sink点&#xff1b;还是如flowdroid构建全局数据流图&#xff0c;并分析存储全局数据可达路径&#xff1b;下面就从其源码上略探一二。1、扫描流程分析其源码前&#xff…

左右伸缩_冬季装修为啥要留伸缩缝?等到天热地板开裂就晚了!合肥人注意下...

后台有粉丝问&#xff1a;冬天真的不适合做装修吗&#xff1f;假的&#xff0c;这句话不要再传了。每个季节都会有利有弊&#xff0c;只不过冬季施工过程中干燥的比较快&#xff0c;装修的时候对施工要求的更严格。就拿伸缩缝来说吧&#xff0c;冬天装修时不注意&#xff0c;等…

红色警戒2修改器原理百科(七)

&#xff08;十五&#xff09;科技全开——前方高能&#xff0c;BOOM 其实这一小节&#xff0c;最晚应该在第四篇的十小节来讲述的&#xff0c;这样是按照一条线索来讲的&#xff0c;容易理解和把握笔者修改时的思路&#xff0c;以一点逐渐扩大。由于笔者的一些原因&#xff08…

wpf click事件在触摸屏上点击第一次没反应_你的PLC和你的触摸屏为什么总是通讯不上?...

三菱PLC全系列资料免费送啦&#xff01;想要撩我(点击上方红字&#xff0c;免费领资料)很多师傅有以下这些问题&#xff0c;其实除去厂家自行定义的通讯方式外&#xff0c;我们在做项目的时候最常用的通讯方式&#xff0c;通讯不上无非就是这几个原因是否选择正确通讯协议(modb…

ubuntu 开放端口_Masscan端口扫描工具使用小手册

1Masscan介绍1.1 Masscan简介1) nmap是端口扫描仪的合法王位&#xff0c;而今天仍然是最通用的选择。但对于纯粹的速度也有一些已经超越了它&#xff0c;包括scanrand&#xff0c;unicornscan&#xff0c;zmap&#xff0c;和现在的masscan(https://github.com/robertdavidgraha…

hdu4277 暴力

hdu4277 暴力 USACO ORZ Time Limit : 5000/1500ms (Java/Other) Memory Limit : 32768/32768K (Java/Other) Total Submission(s) : 4 Accepted Submission(s) : 2 Problem DescriptionLike everyone, cows enjoy variety. Their current fancy is new shapes for pastu…

多线程设置操作时间间隔_听说,有位Java程序员学完这59道多线程面试题之后,直接面上阿里...

前言多线程是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程&#xff0c;进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理或同时多线程处理器。软件多线程&…

svm多分类代码_监督学习——分类算法I

本文是监督学习分类算法的第一部分&#xff0c;简单介绍对样本进行分类的算法&#xff0c;包括判别分析(DA)支持向量机(SVM)随机梯度下降分类(SGD)K近邻分类(KNN)朴素贝叶斯分类(NaiveBayes)判别分析(Discriminant Analysis) 判别分析是基于条件概率的分类方法&#xff0c;可以…

oracle awr报告提取,Oracle AWR报告提取方法

默认情况下&#xff0c;oracle是启用数据库统计收集这项功能(AWR)通过show parameter statistics_level来判断是否启用值为TYPICAL或者ALL表示启用AWR值为BASIC&#xff0c;表示禁用AWR1.当前连接实例的AWR报告提取&#xff1a;?/rdbms/admin/awrrpt以sysdba身份登录。SQL>…

关键词组合工具_SEO学习:如何挖掘行业关键词?关键词挖掘策略和工具推荐

关键词是驱动流量增长的基础&#xff01;挖掘关键词是SEO工作的重中之重。如何快速挖掘到行业相关的所有关键词&#xff1f;我们一起了解下。本文大纲如下&#xff1a;拓展和筛选网站主词挖掘搜索引擎推荐关键词业务相关词的挖掘同行关键词的挖掘关键词组合拓展1、拓展和筛选网…

活体检测python_活体检测很复杂?仅使用opencv就能实现!(附源码)!

摘要&#xff1a; 活体检测在各行各业应用比较广泛&#xff0c;如何实现一个活体检测系统呢&#xff1f;早期实现很困难&#xff0c;现在仅使用opencv即可实现&#xff0c;快来尝试一下吧。什么是活体检测&#xff0c;为什么需要它&#xff1f;随着时代的发展&#xff0c;人脸识…

oracle安装命令大全,oracle安装教程及常用命令

虽然网上已经有很多安装教程了,但还是写一个记录一下自己的安装过程以及遇到问题的解决办法1 安装包下载:去oracle的官网 选择DownLoads->database->oracle database:选择接受选择Windows 64的下载,选择file1,和file2,注意两个文件都要下载,下载时要求登录,有oracle账户的…