JAVA 类生命周期

  如上图所示,Java类的生命周期如图所示,分别为加载、验证、准备、解析、初始化、使用、卸载。其中验证、准备、解析这三个步骤统称为链接。

  加载:JVM根据全限定名来获取一段二进制字节流,将二进制流转化为方法区的运行时数据结构,在内存中生成一个代表该类的Java.lang.Class对象,作为方法区这个类的各种数据访问入口。

  验证:验证是链接的第一步,主要验证内容分为文件格式验证、元数据验证、字节码验证、符号引用验证。

  准备:准备阶段是为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。

  解析:解析阶段是虚拟机将常量池的符号引用替换为直接引用的过程。符号引用所引用的目标不一定在内存中,而转换为直接引用之后,引用直接指向到对应的内存地址。

  初始化:初始化会执行<Clinit>()方法,会对static属性进行赋值,对于static final修饰的基本类型和String类型则在更早的javac编译的时候已经加载到常量池中了。

  经过初始化之后,Java类已经加载完毕。

  

  JVM并没有强制规定什么时候进行类的加载,但是对于初始化规定了有且5种情况必须被初始化:

  1. 遇到new、getstatic、putstatic、invokestatic这四个字节码的执行时,如果类还没有被初始化,则必须被初始化。new为创建对象,剩下三个为操作静态变量。
  2. 使用java.lang.reflect对类进行反射操作的时候,如果该类还没有被加载,则加载该类。
  3. 如果对一个类进行初始化的时候,要先对其父类先进行初始化。
  4. 当虚拟机启动的时候,需要一个Main方法入口,虚拟机会先初始化这个类。
  5. 当使用JDK1.7动态语言支持的时候,如果一个java.lang.invoke.MethodHandle实例最终解析结果为REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,如果对应的类没有初始化、则会被初始化。

  常在笔试题中遇到的就是类记载相关知识,如下面代码,先不看答案想想会打印出什么

例子1:

 public class SuperClass {
public static int value = 123; static {
System.out.println("super class init");
}
} public class SubClass extends SuperClass { static {
System.out.println("Sub class init");
}
} public class ClassInit {
public static void main(String[] args) {
System.out.println(SubClass.value);
}
}

   打印结果如下:

  

  解析:当Main方法执行的时候,不会对SubClass类进行初始化,因为调用静态变量时只会初始化直接定义该变量的类,因此,上述代码只有SuperClass会被初始化。而SubClass并不会被初始化。

  我们稍微修改一个上述代码,将main方法放入子类中执行,执行main方法之后,代码会怎么执行呢?

例子2:

 public class SuperClass {
public static int value = 123; static {
System.out.println("super class init");
}
} public class SubClass extends SuperClass { static {
System.out.println("Sub class init");
} public static void main(String[] args) {
System.out.println(SubClass.value);
}
}

  打印如下图:

  

  解析:根据上述类初始化规定。根据第四条执行main方法时候必须初始当前类,因此触发了SubClass的初始化。根据第三条,如果要触发SubClass,必须先对SuperClass进行初始化。因此会先进行SuperClass的初始化、执行完成后执行SubClass初始化,最后等SubClass初始化完毕,打印出Main方法的中的语句。

例子3:

 public class StaticTest {

     static int b = 200;

     static StaticTest st = new StaticTest();

     static {
System.out.println("1");
} {
System.out.println("2");
} StaticTest() {
System.out.println("3");
System.out.println("a=" + a + ",b=" + b+",c="+c+",d="+d);
} public static void staticFunction() {
System.out.println("4");
} int a = 100; static int c = 300; static final int d=400; public static void main(String[] args) {
staticFunction();
}
}

   执行结果如下:

  

  分析:代码执行之后的结果跟我一开始预想的不大一样,我们按照执行顺序进行分析。当我们执行Main方法的之前,javac需要先将代码编译,在这个时候d属性已经完成了赋值。前面说过,在执行main方法之前,会对main方法所在的类进行初始化。根据属性是否静态,我们大概可以将代码分为两部分:

  1、静态代码

     static int b = 200;

     static StaticTest st = new StaticTest();

     static {
System.out.println("1");
}
public static void staticFunction() {
System.out.println("4");
}

  2、非静态代码:

     {
System.out.println("2");
} StaticTest() {
System.out.println("3");
System.out.println("a=" + a + ",b=" + b + ",c=" + c + ",d=" + d);
} int a = 100;

  把代码分成两部分,主要是为了区分哪些是类初始化里的代码(<clinit>()中的代码,在类初始化的时候执行),哪些对象初始化代码(<init>()中的代码,对象初始化的时候执行)。main方法触发了类的初始化,因此会执行<clinit>()中的代码,执行顺序从上而下,先完成b=200赋值语句,紧接着执行 static StaticTest st = new StaticTest(),而对st的赋值则触发了对象初始化方法,因此会执行<init>()方法,即非静态代码,对象的初始化执行顺序和类初始化执行顺序不相同,类初始化执行顺序  属性初始化 =》代码块 =》方法初始化。因此在非静态代码中执行顺序为: 第10行=》第2行=》第6行=》第7行。所以最早打印出2、3。紧接着打印a、b、c、d数值的时候a、b、d已经完成赋值。完成对象初始化之后,继续执行上面的静态代码,打印出1。等类已经完成了加载,执行main方法,打印出4。

  

双亲委派模型

  在java类加载器对Class进行加载的时候,如果两个类被不同的类加载器加载,则这两个类不相等。通过equals(),instanceof等方法判断结果为false。关于Java的类加载器,大概可以划分为以下几种:

  • 启动类加载器(Bootstrap ClassLoader):Bootstrap ClassLoader是唯一一个通过JVM内部的类加载器,通过C++实现。负责加载<JAVA_HOME>/lib中,或者被-Xbootclasspath参数所指定的路径中的,或者被虚拟机识别的类库记载到虚拟机内存中,仅按照文件名识别,如rt.jar,名字不符合的类库即使放在lib中也不会被加载。启动类加载器无法被Java程序直接使用,如果需要把加载器请求委派给引导类加载,则直接使用null代替即可。
  • 扩展类加载器(Extension ClassLoder):这个加载器负责加载<JAVA_HOME>\lib\ext目录中,或者被java.ext.dirs系统变量所指定的路径中的所有类库。
  • 应用程序类加载器(Application ClassLoader):应用程序类加载器,负责加载用户路径(ClassPath)上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序中没有自义定类加载,则该类加载就是默认的类加载器。

我们的应用程序大部分是通过上面三种类加载器配合完成的,如果有特殊需求,还可以自定义自己的类加载器。包括类记载器在内,各种类加载器的关系可以这样表示。

  上述关系图称为双亲委派模型,除了Bootstrap ClassLoad加载器,其他的类加载器都有自己的父类。当一个类加载器获取到一个类加载任务时,先将该类丢给父加载器加载, 如果父加载器不能加载则自己加载。这种加载方式就称之为双亲委派。如上图所示,自定义类加载器获取到一个加载任务,一层层往上丢,所以最先让启动类加载器加载,如果启动类加载器能加载,则启动类加载器加载,启动类加载器不能加载,则丢给扩展类加载器,如果扩展类加载器不能加载,则丢给应用类加载器,如果应用类加载器不能加载,才丢给自定义加载器加载。

  上述的加载方式看起来特别麻烦,但是却解决了一个很重要的问题。比如自定义类加载器获取到一个Java.lang.Object的任务,则让Bootstrap ClassLoader加载,否则如果用户自己定义了一个Java.lang.Object会跟rt.jar中的类产生冲突,通过双亲委派模型,则用户自己写的Object将永远不会被加载到。

   双亲委派模型是Java虚拟机推荐给开发者的类加载实现,并不是一个强制性约束。在一些情况下双亲委派模型是会被破坏的,比如为了加载JNDI提供者的代码,设计出来的线程上下文加载器。又比如OSGI环境下规则也不大一样。

JAVA 类加载机制学习笔记的更多相关文章

  1. Java SPI机制学习笔记

    最近在阅读框架源代码时,常常看到 SPI 的子包, 忍不住查了下: Service Provider Interface : 服务提供接口. JavaSPI 实际上是“基于接口的编程+策略模式+配置文 ...

  2. Java类加载器学习笔记

    今后一段时间会全面读一下<深入理解Java虚拟机> 在这里先记一下在网上看到的几篇介绍 类加载器 的文章,等读到虚拟机类加载机制再详细介绍. 超详细Java中的ClassLoader详解 ...

  3. <<深入Java虚拟机>>-虚拟机类加载机制-学习笔记

    类加载的时机 遇到new.getstatic.putstatic或invokestatic这4个字节码指令时,如果类没有进行过初始化,则需要先触发其初始化.生成这4条指令最常见的Java场景是:使用n ...

  4. java反射机制学习笔记

    内容引用自:https://www.cnblogs.com/wkrbky/p/6201098.html https://www.cnblogs.com/xumBlog/p/8882489.html,本 ...

  5. java之jvm学习笔记六-十二(实践写自己的安全管理器)(jar包的代码认证和签名) (实践对jar包的代码签名) (策略文件)(策略和保护域) (访问控制器) (访问控制器的栈校验机制) (jvm基本结构)

    java之jvm学习笔记六(实践写自己的安全管理器) 安全管理器SecurityManager里设计的内容实在是非常的庞大,它的核心方法就是checkPerssiom这个方法里又调用 AccessCo ...

  6. JAVA的反射机制学习笔记(二)

    上次写JAVA的反射机制学习笔记(一)的时候,还是7月22号,这些天就瞎忙活了.自己的步伐全然被打乱了~不能继续被动下去.得又一次找到自己的节奏. 4.获取类的Constructor 通过反射机制得到 ...

  7. 《深入理解Java虚拟机》学习笔记

    <深入理解Java虚拟机>学习笔记 一.走近Java JDK(Java Development Kit):包含Java程序设计语言,Java虚拟机,JavaAPI,是用于支持 Java 程 ...

  8. JAVA GUI编程学习笔记目录

    2014年暑假JAVA GUI编程学习笔记目录 1.JAVA之GUI编程概述 2.JAVA之GUI编程布局 3.JAVA之GUI编程Frame窗口 4.JAVA之GUI编程事件监听机制 5.JAVA之 ...

  9. JUC.Lock(锁机制)学习笔记[附详细源码解析]

    锁机制学习笔记 目录: CAS的意义 锁的一些基本原理 ReentrantLock的相关代码结构 两个重要的状态 I.AQS的state(int类型,32位) II.Node的waitStatus 获 ...

随机推荐

  1. SpringMVC(day1搭建SpringWebMvc项目)

    MVC和webMVC的区别 Model(模型) 数据模型,提供要展示的数据,因此包含数据和行为,行为是用来处理这些数据的.不过现在一般都分离开来:Value Object(数据) 和 服务层(行为). ...

  2. ThreadPoolTaskExecutor的配置使用

    版权声明:本文为博主原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/ft305977550/article/de ...

  3. Java中哪个JSON库的解析速度是最快的?

    JSON已经成为当前服务器与WEB应用之间数据传输的公认标准,不过正如许多我们所习以为常的事情一样,你会觉得这是理所当然的便不再深入思考 了.我们很少会去想用到的这些JSON库到底有什么不同,但事实上 ...

  4. 2019 年百度之星·程序设计大赛 - 初赛一 C. Mindis 离散化+dijkstra

    题目传送门 题意:中文题面 思路: 先将所有题目给出的点离散化一下,得到一张n*m的网格,n和m最大都是400,所以我们只需要枚举每个加强的区域,将属于这个区域的边处理一下(所有横着的和竖着的边,暴力 ...

  5. 实现div里面有placeholder形式

    样式中设置content为元素的data-值 p{ color: deepskyblue; } p:before{ content: attr(data-beforeContent); color: ...

  6. Python 输入字符串找(String)下标 没有返回-1

    str = "abcdefg123456"a = input("请输入一个字母或数字:")num = 0result = -1while num < le ...

  7. 【转载】opencl中设备内存

    地址空间限定符 一般的内核代码中,里面的内核参数或声明变量时,都会有地址空间限定符 地址空间限定符,地址空间限定符的主要作用是指出数据应该保存在哪个地方 地址空间限定符有4个: 全局内存: 限定符:_ ...

  8. try-catch 捕捉不到异常

    code: int _tmain(int argc, _TCHAR* argv[]) { cout << "In main." << endl;  //定义 ...

  9. ps快速将白底图片变为透明图片

    方法一: 如果图层有锁图标,则要点击它,然它消失.然后选中魔棒工具,然后点击图片上要透明的区域,按下backspace键即可. 方法二: 转载自:https://blog.csdn.net/sunyi ...

  10. Java 11 发布计划来了,已确定 3个 新特性!!

    Oracle 已经发布了 Java Development Kit 10,下一个版本 JDK 11 也在准备之中了.按照 Java 新的版本发布标准,Java 11 将在 6 个月后到来,现在它还只有 ...