Java类的加载机制( 二 )


2、这里所设置的初始值通常情况下是数据类型默认的零值(如0、0L、null、false等) , 而不是被在Java代码中被显式地赋予的值 。
假设一个类变量的定义为:public static int value = https://www.isolves.com/it/cxkf/yy/JAVA/2019-09-12/3;
那么变量value在准备阶段过后的初始值为0 , 而不是3 , 因为这时候尚未开始执行任何Java方法 , 而把value赋值为3的putstatic指令是在程序编译后 , 存放于类构造器<clinit>()方法之中的 , 所以把value赋值为3的动作将在初始化阶段才会执行 。
这里还需要注意如下几点:

  • 对基本数据类型来说 , 对于类变量(static)和全局变量 , 如果不显式地对其赋值而直接使用 , 则系统会为其赋予默认的零值 , 而对于局部变量来说 , 在使用前必须显式地为其赋值 , 否则编译时不通过 。
  • 对于同时被static和final修饰的常量 , 必须在声明的时候就为其显式地赋值 , 否则编译时不通过;而只被final修饰的常量则既可以在声明时显式地为其赋值 , 也可以在类初始化时显式地为其赋值 , 总之 , 在使用前必须为其显式地赋值 , 系统不会为其赋予默认零值 。
  • 对于引用数据类型reference来说 , 如数组引用、对象引用等 , 如果没有对其进行显式地赋值而直接使用 , 系统都会为其赋予默认的零值 , 即null 。
  • 如果在数组初始化时没有对数组中的各元素赋值 , 那么其中的元素将根据对应的数据类型而被赋予默认的零值 。
 
3、如果类字段的字段属性表中存在ConstantValue属性 , 即同时被final和static修饰 , 那么在准备阶段变量value就会被初始化为ConstValue属性所指定的值 。
假设上面的类变量value被定义为: public static final int value = https://www.isolves.com/it/cxkf/yy/JAVA/2019-09-12/3;
编译时Javac将会为value生成ConstantValue属性 , 在准备阶段虚拟机就会根据ConstantValue的设置将value赋值为3 。回忆上一篇博文中对象被动引用的第2个例子 , 便是这种情况 。我们可以理解为static final常量在编译期就将其结果放入了调用它的类的常量池中
– 解析:把类中的符号引用转换为直接引用
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程 , 解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行 。符号引用就是一组符号来描述目标 , 可以是任何字面量 。
直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄 。
初始化
初始化 , 为类的静态变量赋予正确的初始值 , JVM负责对类进行初始化 , 主要对类变量进行初始化 。在Java中对类变量进行初始值设定有两种方式:
①声明类变量是指定初始值
②使用静态代码块为类变量指定初始值
JVM初始化步骤
1、假如这个类还没有被加载和连接 , 则程序先加载并连接该类
2、假如该类的直接父类还没有被初始化 , 则先初始化其直接父类
3、假如类中有初始化语句 , 则系统依次执行这些初始化语句
类初始化时机
只有当对类的主动使用的时候才会导致类的初始化 , 类的主动使用包括以下六种:
– 创建类的实例 , 也就是new的方式
– 访问某个类或接口的静态变量 , 或者对该静态变量赋值
– 调用类的静态方法
– 反射(如Class.forName(“com.shengsiyuan.Test”))
– 初始化某个类的子类 , 则其父类也会被初始化
– Java虚拟机启动时被标明为启动类的类(Java Test) , 直接使用java.exe命令来运行某个主类
结束生命周期
•在如下几种情况下 , Java虚拟机将结束生命周期
– 执行了System.exit()方法
– 程序正常执行结束
– 程序在执行过程中遇到了异常或错误而异常终止
– 由于操作系统出现错误而导致Java虚拟机进程终止
3、类加载器
寻找类加载器 , 先来一个小例子
package com.neo.classloader;public class ClassLoaderTest { public static void main(String[] args) { ClassLoader loader = Thread.currentThread().getContextClassLoader(); System.out.println(loader); System.out.println(loader.getParent()); System.out.println(loader.getParent().getParent()); }}


推荐阅读