JAVA学习篇--Java类加载
由来:
与普通程序不同的是,Java程序(class文件)并非本地的可执行程序(解释性语言)。
当执行Java程序时。首先执行JVM(Java虚拟机),然后再把Javaclass载入到JVM里头执行,负责载入Javaclass的这部分就ClassLoader。
中文叫做类载入器。
类载入器就好比一个代理,你须要什么。我通过类载入器将你须要的内容返回给你!
类载入器有什么作用?
当程序须要的某个类,那么须要通过类载入器把类的二进制载入到内存中.
解释:
类载入器也是Java类,由于其它是java类的类载入器本身也要被类载入器载入,显然必须有第一个类载入器不是java类,这正是BootStrap。
Java虚拟机中的全部类装载器採用具有父子关系的树形结构进行组织。在实例化每一个类装载器对象时,须要为其指定一个父级类装载器对象或者默认採用系统类装载器为其父级类载入。
Java类载入器:
Java中的类载入器大致能够分成两类。一类是系统提供的。另外一类则是由 Java 应用开发者编写的。
系统提供的类载入器主要有以下三个:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGVqaW5neXVhbjY=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
引导类载入器(bootstrap classloader):
它用来载入 Java 的核心库,是用原生代码来实现的,并不继承自java.lang.ClassLoader。
扩展类载入器(extensions classloader):
它用来载入 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库文件夹。该类载入器在此文件夹里面查找并载入 Java 类。
系统类载入器(system classloader):
它依据 Java 应用的类路径(CLASSPATH)来载入 Java 类。一般来说。Java 应用的类都是由它来完毕载入的。能够过ClassLoader.getSystemClassLoader() 来获取它。
关系:
能够简单理解它们三者之间是父与子的关系。不同的是每一个ClassLoader本身仅仅能分别载入特定位置和文件夹中的类,但它们能够托付其它的类装载器去载入类。这也就是类载入器的托付模式。托付的过程是子类托付父类!
除了系统提供的类载入器以外,开发者能够通过继承java.lang.ClassLoader 类的方式实现自己的类载入器,以满足一些特殊的需求。
类载入器的托付机制(双亲委派机制):
1>当Java虚拟机要载入一个类时,究竟派出哪个类载入器去载入呢?
①首先当前线程的类载入器去载入线程中的第一个类.
②假设类A中引用了类B,Java虚拟机将使用载入类A的类载入器载入类B
③还能够直接调用ClassLoader.loadClass()方法来指定某个类载入器去载入某个类.
2>每一个类载入器载入类时,又先托付给其上级类载入器.
①类装载器一级级托付到BootStrap类载入器,当BootStrap无法载入当前所要载入的类时,然后才一级级回退到子孙类装载器去进行真正的载入。当回退到最初的类装载器时,假设它自己也不能完毕类的装载,那就应报告ClassNotFoundException异常。
简单解释双亲委派机制:
为了安全。java中有String或者其它的核心类,我们想覆盖是覆盖不了的,由于我们自己写的类都属于在classpath路径下。那classpath的装载器都是系统类装载器,即使我们写了一个一模一样的String,它也会一直请求,一直请求到根,假设上面有一个能载入,比如根,那么它就用根载入那个对象给我们返回,我们自己的永远载入不上。这种话我们不至于破坏java的核心库
代码演示样例:
public class Test {
public static void main(String[] args) {
//载入器:sun.misc.Launcher$AppClassLoader
System.out.println(Test.class.getClassLoader().getClass().getName());
//载入器:BootStrap(loader为null的情况)
System.out.println(System.class.getClassLoader());//
System.out.println("----------------查看类载入器的层次结构关系-------------------");
ClassLoader loader = Test.class.getClassLoader();
while(loader != null){
System.out.println(loader.getClass().getName());
loader = loader.getParent();
}
System.out.println(loader);
}
/**
* 执行结果:
* sun.misc.Launcher$AppClassLoader
null
----------------查看类载入器的层次结构关系-------------------
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
null
*/
}
能不能自己写一个类叫java.lang.System?
为了不让我们写System类,类载入採用托付机制,这样能够保证爸爸优先,也就是使用的永远是爸爸的(系统的)System类,而不是我们写的System类.
设计动机:
Java 虚拟机是怎样判定两个 Java类是同样的呢?
Java虚拟机不仅要看类的全名是否同样,还要看载入此类的类载入器是否一样。
仅仅有两者都同样的情况,才觉得两个类是同样的。即便是同样的字节代码,被不同的类载入器载入之后所得到的类。也是不同的。
比方一个Java 类com.example.Sample。编译之后生成了字节代码文件 Sample.class。两个不同的类载入器 ClassLoaderA 和ClassLoaderB 分别读取了这个 Sample.class 文件,并定义出两个 java.lang.Class 类的实例来表示这个类。这两个实例是不同样的。
对于Java 虚拟机来说,它们是不同的类。
了解了以上这一点之后,就能够理解设计动机了,这样的托付机制是为了保证Java 核心库的类型全。
全部 Java 应用都至少须要引用 java.lang.Object 类。也就是说在执行的时候,java.lang.Object这个类须要被载入到 Java 虚拟机中。假设这个载入过程由 Java 应用自己的类载入器来完毕的话。非常可能就存在多个版本号的java.lang.Object类,并且这些类之间是不兼容的。通过托付机制,对于 Java 核心库的类的载入工作由引导类载入器来统一完毕。保证了
Java 应用所使用的都是同一个版本号的Java 核心库的类。是互相兼容的。
类载入器与 Web 容器:
对于执行在容器中的 Web 应用来说。类载入器的实现方式与一般的 Java 应用有所不同。不同的 Web 容器的实现方式也会有所不同。
以Apache Tomcat 来说,每一个 Web 应用都有一个相应的类载入器实例。该类载入器也使用托付机制,所不同的是它是首先尝试去载入某个类。假设找不到再托付给父类载入器。这与一般类载入器的顺序是相反的。这是
Java Servlet 规范中的推荐做法。其目的是使得 Web应用自己的类的优先级高于 Web 容器提供的类。
这样的托付的一个例外是:Java 核心库的类是不在查找范围之内的。
这也是为了保证 Java核心库的类型安全。
Tomcat载入器:
和第一幅图有什么关系呢?
本图是基于第一幅图(java自带机制)的又一层封装,它是基于第一幅图的,仅仅是tomcat在java的基础上自己定义了一些非核心类库的载入器,以供用户使用!
总结:
绝大多数情况下。对于Web应用的开发者不须要考虑与类载入器相关的细节。可是当出现ClassNotFoundException异常我们能大概了解问题所在。
以下给出几条简单的原则:
每一个 Web 应用自己的 Java类文件和使用的库的 jar 包,分别放在 WEB-INF/classes和 WEB-INF/lib文件夹以下。
多个应用共享的 Java 类文件和 jar包。分别放在 Web 容器指定的由全部 Web 应用共享的文件夹以下。
当出现找不到类的错误时,检查当前类的类载入器和当前线程的上下文类载入器是否正确。这些原则也是依据java类载入器和自己定义载入器的实现方式总结而来。须要我们简单的理解!
版权声明:本文博客原创文章,博客,未经同意,不得转载。
JAVA学习篇--Java类加载的更多相关文章
- JAVA学习篇--JAVA两种编程模式控制
在Drp项目中,解说了两种编程模式Model 1和Model2.以下是对这两种模式的简单理解.以及因为Model2是基于MVC架构的模式,就将我们易混淆的MVC与我们之前学的三层架构进行对照学习一下. ...
- [java学习笔记]java语言核心----面向对象之this关键字
一.this关键字 体现:当成员变量和函数的局部变量重名时,可以使用this关键字来区别:在构造函数中调用其它构造函数 原理: 代表的是当前对象. this就是所在函数 ...
- [java学习笔记]java语言核心----面向对象之构造函数
1.构造函数概念 特点: 函数名与类名相同 不用定义返回值类型 没有具体的返回值 作用: 给对象进行初始化 注意: 默认构造函数 多个构造函数是以重载出现的 一个类中如果 ...
- [ Java学习基础 ] Java构造函数
构造方法是类中特殊方法,用来初始化类的实例变量,它在创建对象(new运算符)之后自动调用. Java构造方法的特点如下: 构造方法名必须与类名相同. 构造方法没有任何返回值,包括void. 构造方法只 ...
- [ Java学习基础 ] Java的继承与多态
看到自己写的东西(4.22的随笔[ Java学习基础 ] Java构造函数)第一次达到阅读100+的成就还是挺欣慰的,感谢大家的支持!希望以后能继续和大家共同学习,共同努力,一起进步!共勉! ---- ...
- [ Java学习基础 ] Java的抽象类与接口
一.抽象类 1. 抽象类 Java语言提供了两种类:一种是具体类:另一种是抽象子类. 2. 抽象类概念: 在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的 ...
- Java基础篇(JVM)——类加载机制
这是Java基础篇(JVM)的第二篇文章,紧接着上一篇字节码详解,这篇我们来详解Java的类加载机制,也就是如何把字节码代表的类信息加载进入内存中. 我们知道,不管是根据类新建对象,还是直接使用类变量 ...
- 8.6(java学习笔记)类加载过程及类加载器
一.类加载 1.加载 将class字节码加载到内存中,同时在方法区形成改类运行时数据结构. 同时在堆中产生一个Class对象,反射就是获取这个对象并对其进行操作. 2.链接 2.1验证:验证加载的类信 ...
- 学习Java第一篇——Java 安装及环境搭配
内容提要: 1.下载JDK: 2.安装JDK: 3.配置JDK; 第一,下载JDK: 1.登陆网址:www.oracle.com 2.点击 Downloads 3.选择 Java SE 4.选择 ...
随机推荐
- ext3文件系统反删除利器ext3grep应用实战
推荐:10年技术力作:<高性能Linuxserver构建实战Ⅱ>全网发行,附试读章节和全书实例源代码下载! 一."rm –rf"带来的困惑 国外一份非常著名的Linux ...
- Unity项目优化--开发项目的小经验
原文地址:http://blog.csdn.net/liang_704959721/article/details/8548619 我们主要使用 3dsmax2010 进行制作,输出 FBX的类型导入 ...
- 24L01/SI24R1调试笔记
1.SPI MSB优先,8Bit寄存器地址与内容: 2.寄存器结构与之前使用的LT8900不同,分为R.W寄存器与特殊功能寄存器: 3.特别注意:在TX.RX.RT中断或者轮询后置1,必须写1清零与清 ...
- Java NIO系列教程(三) Buffer(转)
Java NIO中的Buffer用于和NIO通道进行交互.如你所知,数据是从通道读入缓冲区,从缓冲区写入到通道中的. 缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存.这块内存被包装成NIO ...
- springMvc的注解注入方式
springMvc的注解注入方式 最近在看springMvc的源码,看到了该框架的注入注解的部分觉的有点吃力,可能还是对注解的方面的知识还认识的不够深刻,所以特意去学习注解方面的知识.由于本人也是抱着 ...
- 写自己的第二级处理器(3)——Verilog HDL行为语句
我们会继续上传新书<自己动手写处理器>(未公布),今天是第七章,我每星期试试4 2.6 Verilog HDL行为语句 2.6.1 过程语句 Verilog定义的模块一般包含有过程语句,过 ...
- hdu 5060 War
War Time Limit: 8000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submis ...
- 基于PHP的crontab定时任务管理
BY JENNER · 2014年11月10日· 阅读次数:6 linux的crontab一直是server运维.业务开展的利器.但当定时任务增多时,管理和迁移都变得非常麻烦,并且easy出问题.以下 ...
- C语言优化实例:为了消除嵌套switch-case聪明的做法
我们有可能会写出或者遇到类似这种代码: C/C++ switch (expr1) { case label11: switch (expr2) { case label21: // do someth ...
- DataTable转化为List
public List<T> ConvertToList<T>(DataTable dt) where T : new() { // 定义集 ...