每个类编译后产生一个Class对象,存储在.class文件中,JVM使用类加载器(Class Loader)来加载类的字节码文件(.class),类加载器实质上是一条类加载器链,一般的,我们只会用到一个原生的类加载器AppClassLoader,它只加载Java API等可信类,通常只是在本地磁盘中加载,这些类一般就够我们使用了。如果我们需要从远程网络或数据库中下载.class字节码文件,那就需要我们来挂载额外的类加载器。

一般来说,类加载器是按照树形的层次结构组织的,每个加载器都有一个父类加载器。另外,每个类加载器都支持代理模式,即可以自己完成Java类的加载工作,也可以代理给其它类加载器。

ClassLoader中的几个实现类

1、Bootstrap ClassLoader 这个是JVM加载自身工作需要的类,完全由JVM自己来控制,外部无法访问到这个;

2、ExtClassLoader比较特殊的,服务的特定目标在System.getProperty("java.ext.dirs");

3、AppClassLoader,父类是ExtClassLoader,java中参数-classpath中的类都可以被这个类加载器加载;

4、URLClassLoader,一般这个类帮我们实现了大部分的工作,自定义可以继承这个类,这样仅仅在需要的地方做修改就行了;

类加载器的加载顺序有两种,一种是父类优先策略,一种是是自己优先策略,父类优先策略是比较一般的情况(如JDK采用的就是这种方式),在这种策略下,类在加载某个Java类之前,会尝试代理给其父类加载器,只有当父类加载器找不到时,才尝试子类加载器去加载,如果找到了,自己就不用加载。自己优先的策略与父类优先相反,它会首先尝试自己加载,如果找到了就不用父类加载器去加载,只有找不到的时候才要父类加载器去加载,这种在web容器(如tomcat)中比较常见。

动态加载

不管使用什么样的类加载器,类都是在第一次被用到时,动态加载到JVM的。这句话有两层含义:

  1. Java程序在运行时并不一定被完整加载,只有当发现该类还没有加载时,才去本地或远程查找类的.class文件并验证和加载(赖加载);
  2. 当程序创建了第一个对类的静态成员的引用(如类的静态变量、静态方法、构造方法——构造方法也是静态的)时,才会加载该类。Java的这个特性叫做:动态加载

JVM加载clas文件到内存的方式

1、显示加载:不通过代码里的ClassLoader调用,而是JVM来自动加载类到内存中的方式;

1.1、通过Class中的forName;

1.2、通过ClassLoader中的loadClass

1.3、通过ClasLoader中的findSystemClass

2、隐身加载:通过代码中ClassLoader来加载的方式;

如何加载class文件

1)加载(Loading),由类加载器执行,查找字节码,并创建一个Class对象(只是创建);

a)通过类的全名产生对应类的二进制数据流。(注意,如果没找到对应类文件,只有在类实际使用时才抛出错误。)

b)分析并将这些二进制数据流转换为方法区(JVM 的架构:方法区、堆,栈,本地方法栈,pc
寄存器)特定的数据结构(这些数据结构是实现有关的,不同 JVM
有不同实现)。这里处理了部分检验,比如类文件的魔数的验证,检查文件是否过长或者过短,确定是否有父类(除了 Obecjt 类)。

c)创建对应类的 java.lang.Class 实例(注意,有了对应的 Class 实例,并不意味着这个类已经完成了加载链链接!)。

2)链接(Linking),验证字节码,为静态域分配存储空间(只是分配,并不初始化该存储空间),解析该类创建所需要的对其它类的应用;

a)验证(verification)

链接的第三部解析会把类中成员方法、成员变量、类和接口的符号引用替换为直接引用,而在这之前,需要检测被引用的类型正确性和接入属性是否正确(就是
public ,private 的的问题),诸如检查 final class
又没有被继承,检查静态变量的正确性等等。(注意到实际上有一部分验证过程已经在加载的过程中执行了。)

b)准备(preparation)

对类的成员变量分配空间。虽然有初始值,但这个时候不会对他们进行初始化(因为这里不会执行任何 Java 代码)。具体如下:所有原始类型的值都为
0。如 float: 0f, int: 0, boolean: 0(注意 boolean 底层实现大多使用 int),引用类型则为
null。值得注意的是,JVM 可能会在这个时期给一些有助于程序运行效率提高的数据结构分配空间。比如方发表(类似与
C++中的虚函数表,参见另一篇博文《Java:方法的虚分派和方法表》)。

c)解析(Resolution)

首先,为类、接口、方法、成员变量的符号引用定位直接引用(如果符号引用先到常量池中寻找符号,再找先应的类型,无疑会耗费更多时间),完成内存结构的布局。

然后,这一步是可选的。可以在符号引用第一次被使用时完成,即所谓的延迟解析(late
resolution)。但对用户而言,这一步永远是延迟解析的,即使运行时会执行 early
resolution,但程序不会显示的在第一次判断出错误时抛出错误,而会在对应的类第一次主动使用的时候抛出错误!

最后,这一步与之后的类初始化是不冲突的,并非一定要所有的解析结束以后才执行类的初始化。不同的 JVM 实现不同。详情见另一篇博文《Java 类加载的延迟初始化》

3)初始化(Initialization)。

动态加载类:

  1. public class BeanUtilsTest
  2. {
  3. public static void main(String[] args)
  4. throws Exception
  5. {
  6. Class clz = Class.forName("com.ai.redis.A");
  7. }
  8. }
  9. class A
  10. {
  11. public static int VALUE;
  12. static
  13. {
  14. System.out.println("run parent static code.");
  15. }
  16. }

输出结果:打印run parent static code.

类.class:

  1. public class BeanUtilsTest
  2. {
  3. public static void main(String[] args)
  4. throws Exception
  5. {
  6. Class clz1 = A.class;
  7. }
  8. }
  9. class A
  10. {
  11. public static int VALUE;
  12. static
  13. {
  14. System.out.println("run parent static code.");
  15. }
  16. }

输出结果:啥也没有。

通过以上比较,下面这段代码应该知道打印什么了吧。

  1. public class BeanUtilsTest
  2. {
  3. public static void main(String[] args)
  4. throws Exception
  5. {
  6. System.out.println(A.VALUE);
  7. }
  8. }
  9. class A
  10. {
  11. public static final int VALUE = 10;
  12. static
  13. {
  14. System.out.println("run parent static code.");
  15. }
  16. }

输出结果:10

有人要问了,为什么不打印run parent static code.因为VALUE变量是在编译时就已经确定的一个常量值跟类.class文件是一个道理,所以不打印。

注:编译时常量必须满足3个条件:static的,final的,常量。

  1. <pre class="html" name="code">    static int a;
  2. final int b;
  3. static final int c = Math.abs(10);
  4. static final int d;
  5. static
  6. {
  7. d = 5;
  8. }

PS:

为什么接口不能定义成员变量,而只能定义 final static 变量。

  • 1.接口是不可实例化,它的所有元素都不必是实例(对象)层面的。static 满足了这一点。
  • 2.如果接口的变量能被修改,那么一旦一个子类实现了这个接口,并修改了接口中的非 final 变量,而该子类的子类再次修改这个非 final 的变量后,造成的结果就是虽然实现了相同的接口,但接口中的变量值是不一样的。

综上述,static final 更适合于接口。

参考:

1、《通过类字面常量解释接口常量为什么只能定义为 static final,类加载过程—Thinking in java》

2、http://blog.csdn.net/biaobiaoqi/article/details/6909141

3、http://www.cnblogs.com/zhguang/p/3154584.html

4、http://iamzhongyong.iteye.com/blog/2091549

java中的类加载器ClassLoader和类初始化的更多相关文章

  1. Java中的类加载器----ClassLoader

    1.简单的讲类加载器就是加载类. 在一个类要被执行时,首先会被从硬盘中加载到内存中,这个任务就是由类加载器来完成,如果加载不成功时,类是无法被执行的.类加载器执行的都是字节码二进制文件.   帮助文档 ...

  2. Java中的类加载器

    转载:http://blog.csdn.net/zhangjg_blog/article/details/16102131 从java的动态性到类加载机制   我们知道,Java是一种动态语言.那么怎 ...

  3. Java中的类加载器以及Tomcat的类加载机制

    在加载阶段,虚拟机需要完成以下三件事情: 1.通过一个类的全限定名来获取其定义的二进制字节流. 2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构. 3.在Java堆中生成一个代表这个类 ...

  4. Java中的类加载器--Class loader

    学习一下Java中的类加载器,这个是比较底层的东西,好好学习.理解一下.  一.类加载器的介绍 1.类加载器:就是加载类的工具,在java程序中用到一个类,java虚拟机首先要把这个类的字节码加载到内 ...

  5. [读书笔记]java中的类加载器

    以下内容大多来自周志明的<深入理解Java虚拟机>. 类加载器是java的一项创新,也是java流行的重要原因之一,它最初是为了满足java applet的需求而开发出来. 什么是appl ...

  6. 关于java中的类加载器

    什么是类加载器? 类加载器是专门负责加载类的命令或者说工具 ClassLoader java中的3个类加载器 JDK中自带了3个类加载器 启动类加载器 扩展类加载器 应用类加载器 假设有这样一段代码 ...

  7. JAVA基础_类加载器

    什么是类加载器 类加载器是Java语言在1.0版本就引入的.最初是为了满足JavaApplet需要.现在类加载器在Web容器和OSGI中得到了广泛的应用,一般来说,Java应用的开发人员不需要直接同类 ...

  8. jvm之java类加载机制和类加载器(ClassLoader),方法区结构,堆中实例对象结构的详解

    一.类加载或类初始化:当程序主动使用某个类时,如果该类还未被加载到内存中,则JVM会通过加载.连接.初始化3个步骤来对该类进行初始化.如果没有意外,JVM将会连续完成3个步骤. 二.类加载时机:  1 ...

  9. Java虚拟机学习(5):类加载器(ClassLoader

    类加载器 类加载器(ClassLoader)用来加载 class字节码到 Java 虚拟机中.一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源文件在经过 Javac之后就被转换成 ...

随机推荐

  1. 【转】Ubuntu 18.04安装小记

    我的电脑是神舟z7 kp7s1,显卡1060,尝试了Ubuntu的16.04,安装完进入界面总是卡死了,后面换18.04依然无解,和版本无关,而是因为英伟达的显卡问题. 参考了如下这篇文章:Ubunt ...

  2. jquery.validate1.9.0前台验证使用

    一.利用jquery.form插件提交表单方法使用jquery.validate插件 现象:当提交表单时,即使前台未验证通过,也照常提交表单. 解决办法: $('#myForm').submit(fu ...

  3. 大端和小端(Big endian and Little endian)

    一.大端和小端的问题 对于整型.长整型等数据类型,Big endian 认为第一个字节是最高位字节(按照从低地址到高地址的顺序存放数据的高位字节到低位字节):而 Little endian 则相反,它 ...

  4. javascript数组去重的4个方法(转)

    原文地址:http://blog.csdn.net/chengxuyuan20100425/article/details/8497277 面试前端必须准备的一个问题:怎样去掉Javascript的A ...

  5. Pairwise

    FCC题目:找到你的另一半 有一个能力数组[7,9,11,13,15],按照最佳组合值为20来计算,只有7+13和9+11两种组合.而7在数组的索引为0,13在数组的索引为3,9在数组的索引为1,11 ...

  6. 更改DNS轻松访问google.com,FaceBook,Youtube等

    将默认的Dns更改为42.120.21.30即可打开 https://www.google.com/ https://www.facebook.com/ https://www.youtube.com ...

  7. android学习的网站收集

    1. http://mob.com/#/index 提供分享等统一解决方案 2. http://bbs.apkbus.com/explore/ 这个类似的quroa问答模块,覆盖不错.就是人气,稍差. ...

  8. Some web Address

    1.可视化算法(Data Structure Visualizations) https://www.cs.usfca.edu/~galles/visualization/Algorithms.htm ...

  9. std::nothrow

    std::nothrow 1.在内存不足时,new (std::nothrow)并不抛出异常,而是将指针置NULL. 若不使用std::nothrow,则分配失败时程序直接抛出异常. 2.使用方式: ...

  10. Makefile之写demo时的通用Makefile写法

    Makefile之写demo时的通用Makefile写法[日期:2013-05-22] 来源:CSDN  作者:gqb666 [字体:大 中 小] 前面的一篇文章Makefile之大型工程项目子目录M ...