【Java原理系列1】Java类加载机制
Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这个过程被称作虚拟机的类加载机制。
类的生命周期
类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载,验证,准备,解析,初始化,使用,卸载这7个阶段.其中其中验证、准备、解析3个部分统称为连接.
虚拟机规范有且只有5种情况必须对类进行初始化:
-
遇到有new、getstatic、putstatic或者invokestatic这4个字节码指令时,如果类没有初始化,则开始初始化。
-
使用java.lang.reflect包的方法对类进行反射调用时,如果类没有进行过初始化,则需要先触发其初始化。
-
当初始化一个类时,如果发现其父类还没有初始化,则需要先触发父类的初始化。
-
当虚拟机启动时,用户需要指定一个执行的主类(包含main()方法的那个类),虚拟机会先初始化这个类。
-
当使用JDK1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle 实例最后的解析结果为REF_getStatic、REF_putstatic、REF_invokestatic的方法句柄,并且这个方法的对应类没有初始化,则需要先触发其初始化。
类加载过程
-
加载,加载过程中虚拟机需要完成3件事:
-
-
通过一个类的全限定名来获取此类的二进制字节流
-
将这个字节流所代表的的静态存储结构转化为方法区运行时的数据结构
-
在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据结构的访问入口
-
-
验证,确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害到虚拟机自身的安全。
-
-
文件格式验证:验证字节流是否符合Class文件格式的规范,并且能被当前版本虚拟机处理。比如 是否以0xCAFEBABE开头,主次版本号是否在当前虚拟机处理范围内。
-
元数据验证:对字节码描述的信息进行语义分析,以确保其符合java语言规范的要求,比如是否有父类,是否继承了不允许被继承的方法(final修饰了)等。
-
字节码验证:这个阶段将对类的方法体进行校验分析,保证被校验类的方法不会趉危害虚拟机安全的事件。
-
-
准备:正式为类变量分配内存并设置类变量的初始值,这些类变量所使用的的内存都将在方法区中进行分配。这个时候的内存分配仅包括类变量(被static修饰的变量,而不包括实例变量,实例变量在对象初始化的时候一起分配在堆中)。
-
解析:是虚拟机将常量池的符号引用替换为直接引用的过程。
-
初始化:初始化阶段,才真正开始执行类中定义的java程序代码(或者说是字节码)
以下几种情况不会执行类初始化
-
通过子类引用父类的静态字段,只会触发父类的初始化,而不会触发子类的初始化。
-
定义对象数组,不会触发该类的初始化。
-
常量在编译期间会存入调用类的常量池中,本质上并没有直接引用定义常量的类,不会触发定义常量所在的类。
-
通过类名获取 Class 对象,不会触发类的初始化。
-
通过 Class.forName 加载指定类时,如果指定参数 initialize 为 false 时,也不会触发类初始化,其实这个参数是告诉虚拟机,是否要对类进行初始化。
-
通过 ClassLoader 默认的 loadClass 方法,也不会触发初始化动作。
Java 类初始化顺序说明
普通类:
-
静态属性:static开头定义的属性,静态方法块:static{} 圈起来的方法块
-
普通属性:未带static定义的属性,普通方法块:{} 圈起来的方法块
-
构造函数:类名相同的方法
-
方法:普通方法
继承的子类:
-
父类的静态变量,父类的静态代码块
-
子类的静态变量,子类的静态代码块
-
父类的普通变量,父类的普通代码块
-
父类的构造函数
-
子类的普通变量,子类的普通代码块
-
子类的构造函数
一个练手的case
执行main方法后,控制台打印
答案: 类加载时先加载static开头定义的属性,静态方法块:static{} 圈起来的方法块,然后再加载未带static定义的属性,普通方法块:{} 圈起来的方法块,其次是加载构造函数,最后加载普通方法。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!