标签:source 描述 安全 执行 动态绑定 意义 tst java虚拟机 语句块
虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。
类的生命周期如下:
加载 --> 验证 --> 准备 --> 解析 --> 初始化 --> 使用 --> 卸载
其中验证、准备、解析三个阶段属于连接过程。解析可以发生在初始化之后,这是因为Java支持动态绑定,或者说运行时绑定。
发生下面5种情况,必须立即对类进行初始化:
public final static String HELLO = "Hello";
)以上5种情况,称为对一个类的主动引用。其余情况都被称为被动引用。来看几中被动引用的情况;
MyObject[] objs = new MyObject[10]
public final static String HELLO = "Hello"
,当调用HELLO时,并不会触发初始化。接口也有初始化过程,和类的初始化的区别在于:接口在初始化时,不要求求父接口必须先初始化完毕,而是等用到父接口时,在初始化父接口。
加载阶段要完成以下三件事:
非数组类的加载可以使用系统提供的引导类加载器,也可以使用自定义的类加载器去完成。
数组类本身不通过类加载器创建,有Java虚拟机直接创建,但是数组类的元素类型(如Object[]
中的Object,Object[][]
中的Object)最终还是由类加载器创建。
元素类型分两种,引用类型和基本类型。
验证阶段包括以下4个检验:
正式为类变量分配内存并设置类变量(被static修饰)的初始值,初始值一般为“零值”,比如对于int型来说是0,对于boolean来说是false。下面的例子:
public static int val = 9;
在准备阶段后初始值为0而不是9,val被赋值为9是在初始化阶段。
但如果是
public final static int val = 9;
因为有final修饰,所有在准备阶段就会将val赋值为9。
虚拟机将常量池内的符号引用替换为直接引用的过程。
解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄、和调用点限定符7类符号引用进行。
如果当前类是D,要把一个从未解析过的符号引用N解析成一个类或者接口的直接引用,需要如下三个步骤:
Integer[10]
。则按照上述规则加载数组元素类型(在这个例子中就是Integer),然后会由虚拟机生成一个代表此数组维度和对象(在这里分别是10和Integer)的数组对象。首先会解析字段所属的类或接口的符号引用。如果解析成功,将字段所属的类或接口用C表示。之后对C进行后续字段的搜索
查找过程中成功返回引用后,将会对这个字段进行权限验证,如果发现不具备对字段的访问权限,将抛出异常。
首先会解析方法所属的类或接口的符号引用。如果解析成功,将字段所属的类用C表示。之后对C
如果查找成功并成功返回了直接引用,会对这个方法进行权限验证,如果发现不具备对此方法的访问权限,抛出异常。
首先会解析方法所属的类或接口的符号引用。如果解析成功,将字段所属的接口用C表示。之后对C
因为接口中的方法都是public,所以不存在像上面一样的访问权限问题。
初始化阶段才真正开始执行类中定义的Java程序代码。初始化阶段是执行类构造器(<clinit>()
方法的过程(不是实例构造器<init>()
)。
public static int val = 9;
在准备阶段val是0,而在初始化阶段val才被赋值为9。
<clinit>()
的产生:有编译期自动收集类中的所有类变量的赋值动作和静态语句块即static{}块的语句合并产生。自动收集的顺序和语句在文件中出现的顺序一致。<clinit>()
不同于<init>()
,不需要显式调用父类构造器,虚拟机保证子类的<clinit>()
在执行前,父类的<clinit>()
已执行完毕;<clinit>()
对于类或接口不是必需的,如果一个类中没有静态语句块,也没有对类变量的赋值操作,就不会为这个类生成<clinit>()
方法。<clinit>()
先执行,因此父类的静态静态语句块会先于子类的执行;<clinit>()
方法,但是和类不同的是,执行接口的<clinit>()
不会触发父类接口的<clinit>()
,只有当使用到父类中定义的变量时,父类才会初始化;<clinit>()
方法,其他线程会阻塞,直到该线程的<clinit>()
执行完毕。类加载器:通过一个类的全限定名来获取描述此类的二进制字节流。
任何一个类,都需要由加载它的类加载器和类本身一同确定在Java虚拟机的唯一性,每一个类加载器都拥有一个独立的类名称空间,通俗地说:比较两个类是否“相等”,只有在这两个类是由同一个类加载器的前提下才有意义。即使两个类来自同一个Class文件,且被同一个Java虚拟机加载,只要它们不是被同一个类加载器加载的,这两个类就不相等。
对JVM来讲,只存在两种不同的类加载器:
再细分的话
应用程序都是由这三种类加载器互相配合进行加载的,这些类加载器的关系一般是以双亲委派模型呈现。双亲委派模型:除了顶层的启动类加载器外,其余类加载器都应当有自己的父类加载器。这里的父子关系一般是以组合关系而不是继承。
双亲委派模型的工作过程:一个类加载器收到了类加载的请求,它首先不会自己去加载这个类,而是委派给父类去完成,所以最终的请求被传送到了顶层的启动类加载器中;只有当父类反馈自己无法完成这个加载请求时,子类才会尝试自己去加载。
双亲委派模型的优点:Java类随着它的类加载器一起具备了一种带有优先级的层次关系。比如java.lang.Object类,存放在rt.jar中,无论哪一个类加载器都要加载这个类,最后都委派给了顶端的启动类加载器进行加载了,所以Object类在程序的各种加载器环境种都是同一个类。
双亲委派模型的实现:先检查是否被加载过,若没有就调用父加载器的loadClass()
方法,若父加载器为空就使用启动类加载器作为父加载器,如果父加载器加载失败,抛出异常后,在调用自己的findClass()
进行加载。
by @sunhaiyu
2018.6.11
标签:source 描述 安全 执行 动态绑定 意义 tst java虚拟机 语句块
原文地址:https://www.cnblogs.com/sun-haiyu/p/9204282.html