类加载器

在了解Java的机制之前,需要先了解类在JVM(Java虚拟机)中是如何加载的,这对后面理解java其它机制将有重要作用。

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

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

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

动态加载

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

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

需要区分加载和初始化的区别,加载了一个类的.class文件,不以为着该Class对象被初始化,事实上,一个类的初始化包括3个步骤:

  • 加载(Loading),由类加载器执行,查找字节码,并创建一个Class对象(只是创建);
  • 链接(Linking),验证字节码,为静态域分配存储空间(只是分配,并不初始化该存储空间),解析该类创建所需要的对其它类的应用;
  • 初始化(Initialization),首先执行静态初始化块static{},初始化静态变量,执行静态方法(如构造方法)。

链接

Java在加载了类之后,需要进行链接的步骤,链接简单地说,就是将已经加载的java二进制代码组合到JVM运行状态中去。它包括3个步骤:

  1. 验证(Verification),验证是保证二进制字节码在结构上的正确性,具体来说,工作包括检测类型正确性,接入属性正确性(public、private),检查final class 没有被继承,检查静态变量的正确性等。
  2. 准备(Preparation),准备阶段主要是创建静态域,分配空间,给这些域设默认值,需要注意的是两点:一个是在准备阶段不会执行任何代码,仅仅是设置默认值,二个是这些默认值是这样分配的,原生类型全部设为0,如:float:0f,int 0, long 0L, boolean:0(布尔类型也是0),其它引用类型为null。
  3. 解析(Resolution),解析的过程就是对类中的接口、类、方法、变量的符号引用进行解析并定位,解析成直接引用(符号引用就是编码是用字符串表示某个变量、接口的位置,直接引用就是根据符号引用翻译出来的地址),并保证这些类被正确的找到。解析的过程可能导致其它的类被加载。需要注意的是,根据不同的解析策略,这一步不一定是必须的,有些解析策略在解析时递归的把所有引用解析,这是early resolution,要求所有引用都必须存在;还有一种策略是late resolution,这也是Oracle 的JDK所采取的策略,即在类只是被引用了,还没有被真正用到时,并不进行解析,只有当真正用到了,才去加载和解析这个类。

初始化

注 意:在《Java编程思想》中,说static{}子句是在类第一次加载时执行且执行一次(可能是笔误或翻译错误,因为此书的例子显示static是在第 一次初始化时执行的),《Java深度历险》中说 static{}是在第一次实例化时执行且执行一次,这两种应该都是错误的,static{}是在第一次初始化时执行,且只执行一次;用下面的代码可以判 定出来:

package myblog.classloader;

/**
* @project MyBlog
* @create 2013年6月18日 下午7:00:45
* @version 1.0.0
* @author 张广
*/
public class Toy {
private String name; public static final int price=10; static {
System.out.println("Initializing");
} Toy() {
System.out.println("Building");
}
Toy(String name) {
this.setName(name);
} public static String playToy(String player) {
String msg = buildMsg(player);
System.out.println(msg);
return msg;
}
private String buildMsg(String player) {
String msg = player + " plays " + name;
return msg;
}
}
// 对上面的类,执行下面的代码:
Class c = Class.forName("myblog.rtti.Toy");
// c.newInstance();

可以看到,不实例化,只执行forName初始化时,仍然会执行static{}子句,但不执行构造方法,因此输出的只有Initializing,没有Building。

关于初始化,@阿春阿晓 在本文的评论中给出了很详细的场景,感谢@阿春阿晓:

根据java虚拟机规范,所有java虚拟机实现必须在每个类或接口被java程序首次主动使用时才初始化。

主动使用有以下6种:
1) 创建类的实例
2) 访问某个类或者接口的静态变量,或者对该静态变量赋值(如果访问静态编译时常量(即编译时可以确定值的常量)不会导致类的初始化)
3) 调用类的静态方法
4) 反射(Class.forName(xxx.xxx.xxx))
5) 初始化一个类的子类(相当于对父类的主动使用),不过直接通过子类引用父类元素,不会引起子类的初始化(参见示例6)
6) Java虚拟机被标明为启动类的类(包含main方法的)

类与接口的初始化不同,如果一个类被初始化,则其父类或父接口也会被初始化,但如果一个接口初始化,则不会引起其父接口的初始化。

类的被动引用(不会发生类的初始化)
1)当访问一个静态域时,只有真正声明这个域的类才会被初始化
  • 通过子类引用父类的静态变量,不会导致子类初始化
2) 通过数组定义类引用,不会触发此类的初始化
3)引用常量不会触发此类的初始化(常量在编译阶段就存入调用类的常量池中了)

注意:链接阶段是在类加载之后就执行的,初始化在类加载之后不一定执行。

网址:https://www.cnblogs.com/zhguang/p/3154584.html#linking

java类从加载、连接到初始化过程的更多相关文章

  1. JAVA类的加载、连接与初始化

    JAVA类的加载.连接与初始化 类的声明周期总共分为5个步骤1.加载2.连接3.初始化4.使用5.卸载 当java程序需要某个类的时候,java虚拟机会确保这个类已经被加载.连接和初始化,而连接这个类 ...

  2. Java类的加载、链接和初始化

    一.Java的类加载机制回顾与总结: 我们知道一个Java类要想运行,必须由jvm将其装载到内存中才能运行,装载的目的就是把Java字节代码转换成JVM中的java.lang.Class类的对象.这样 ...

  3. java 类的加载,链接,初始化

    本篇的话题,讨论Java类的加载.链接和初始化.Java字节代码的表现形式是字节数组(byte[]),而Java类在JVM中的表现形式是java.lang.Class类的对象.一个Java类从字节代码 ...

  4. Java类的加载 链接 初始化

    原文地址 Java类的加载.链接和初始化.Java字节代码的表现形式是字节数组(byte[]),而Java类在JVM中的表现形式是java.lang.Class类的对象.一个Java类从字节代码到能够 ...

  5. jvm系列(一):java类的加载机制

    java类的加载机制 1.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装 ...

  6. 别翻了,这篇文章绝对让你深刻理解java类的加载以及ClassLoader源码分析【JVM篇二】

    目录 1.什么是类的加载(类初始化) 2.类的生命周期 3.接口的加载过程 4.解开开篇的面试题 5.理解首次主动使用 6.类加载器 7.关于命名空间 8.JVM类加载机制 9.双亲委派模型 10.C ...

  7. JVM(1):Java 类的加载机制

    原文出处: 纯洁的微笑 java类的加载机制 1.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang. ...

  8. Java类的加载过程-重点!!

    java类的加载过程有以下几步共同完成: 加载->连接->初始化.连接又分为验证.准备.解析 一个非数组类的加载阶段(加载阶段获取类的二进制字节流的动作)是可控性最强的阶段,这一步我们可以 ...

  9. 【Java基础】Java类的加载和对象创建流程的详细分析

    相信我们在面试Java的时候总会有一些公司要做笔试题目的,而Java类的加载和对象创建流程的知识点也是常见的题目之一.接下来通过实例详细的分析一下. 实例问题 实例代码 Parent类 package ...

随机推荐

  1. thinkphp5.0.19 表单令牌

    助手函数token() [F:\phpStudy\WWW\csweb\thinkphp\helper.php] request类token()方法 [F:\phpStudy\WWW\csweb\thi ...

  2. 第一次接触oracle

    登录 SQLPLUS cmd sqlplus [用户名]/[密码][@数据库] [参数] sqlplus sys/orcl as sysdba -- 登录 sys 用户,必须指定 sysdba 或 s ...

  3. C语言realpath

    表头文件:     #include <limits.h>     #include <stdlib.h> 函数原型:     char *realpath(const cha ...

  4. java多线程sleep,wait,yield方法区别

    sleep() 方法sleep()的作用是在指定的毫秒数内让当前“正在执行的线程”休眠(暂停执行).这个“正在执行的线程”是指this.currentThread()返回的线程.sleep方法有两个重 ...

  5. hdu 3572 : Task Schedule (网络流)

    题目链接 题意: 有M个机器,N个任务 对第i个任务,需要在[Si,Ei]这段时间内恰有Pi天被process 每天最多有M个机器同时工作 每一天,一个任务若被process,那么它恰占用一个机器. ...

  6. c++ string去除左右空格

    res.substr(res.find_first_not_of(' '),res.find_last_not_of(' ') + 1)

  7. get和post请求方式的区别,常见状态码的整理

    get和post的区别 get和post是什么? HTTP协议中的两种发送请求的方法.get从指定的资源请求数据: post向指定的资源提交要被处理的数据. HTTP是什么? 超文本传输协议(HTTP ...

  8. 事物 @Transactional

    转载:http://www.cnblogs.com/kristain/articles/2038397.html 一.什么是事务 事务是访问数据库的一个操作序列,数据库应用系统通过事务集来完成对数据库 ...

  9. 原来程序是这样从NandFlash拷贝并跳转到SDRAM的

    重新看了一下FL2440的BootLoader,终于把程序是怎样从Nandflash拷贝并跳转到SDRAM的过程弄清楚了,在这边做一下笔记.先上张图: 1. 当S3C2440被配置成从Nand Fla ...

  10. iOS9 3DTouch 之 Home Screen Quick Actions

    最后更新:2016-12-18 测试环境: Xcode8.1 一.前言 iOS9 已经过去一年了,3D Touch也在项目中实战过,但一直没有总结一下,现在新的项目也用到了3D Touch, 网上找了 ...