类的加载

当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过加载、连接、初始化三个步骤来对该类进行初始化。

类加载指的是将类的class文件读入内存,并为之创建一java.lang.Class对象,也就是说,当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象。

类的连接

当类被加载之后,系统为之生成一个对应的Class对象,接着将会进入连接阶段,连接阶段负责把类的二进制数据合并到JRE中。

类连接的三个阶段:

  1. 验证:验证阶段用于检验被加载的类是否有正确的内部结构,并和其他类协调一致。
  2. 准备:类准备阶段则负责为类的类变量分配内存,并设置默认初始值。
  3. 解析:将类的二进制数据中的符号引用替换成直接引用。

类的初始化

虚拟机负责对类进行初始化,主要就是对类变量进行初始化。

JVM初始化一个类包含如下几个步骤:

  1. 假如这个类还没有被加载和连接,则程序先加载并连接该类。
  2. 假如该类的直接父类还没有被初始化,则先初始化其直接父类。
  3. 假如类中有初始化语句,则系统依次执行这些初始化语句。

类的初始化时机

当java程序首次通过下面6种方式来使用某个类或接口时,系统就会初始化该类或接口。

  • 创建类的实例。(new、反射、反序列化)
  • 调用某个类的静态方法。
  • 访问某个类或接口的类变量,或为该类变量赋值。
  • 使用反射强制创建某个类或接口对应的java.lang.Class对象。例如:Class.forName(“”);
  • 初始化某个类的子类。
  • 直接使用java.exe命令运行某个主类。

特殊情况;

对于一个final型的类变量,如果该类变量的值在编译时就可以确定下来,那么这个类变量相当于“宏变量”。

java编译器会在编译时直接把这个类变量出现的地方替换成它的值,因此即使程序使用该静态类变量,也不会导致该类的初始化。

类加载器

类加载器加载Class大致要经过如下8个步骤:

  1. 检测此Class是否载入过(即在缓存区中是否有此Class),如果有则直接进入第8步。
  2. 如果父类加载器不存在,则跳到第4步执行;如果父类加载器存在,则接着执行第3步。
  3. 请求使用父类加载器来载入目标类,如果成功载入则跳到第8步。
  4. 请求使用跟雷加载器来载入目标类,如果成功载入则跳到第8步。
  5. 当前类加载器尝试寻找Class文件,如果找到则执行第6步,如果找不到则跳到第7步。
  6. 从文件中载入Class,成功载入后跳到第8步。
  7. 抛出ClassNotFoundException异常。
  8. 返回对应的java.lang.Class对象。

其中,第5、6步允许重写ClassLoader的findClass()方法来实现自己的载入策略,甚至重写loadClass()方法来实现自己的载入过程。

获得Class对象

每个类被加载之后,系统就会为该类生成一个对应的Class对象,通过该Class对象就可以访问到JVM中的这个类。

在java程序中获得Class对象通常有如下三种方式:

  1. 使用Class类的forName(String calzzName)静态方法。该方法需要传入字符串参数,该字符串参数的值是某个类的全限定类名(必须添加完整包名)。
  2. 调用某个类的class属性来获取该类对应的Class对象。例如Persen.class将返回Person类对应的Class对象。
  3. 调用某个对象的getClass()方法。该方法将会返回该对象所属类对应的Class对象。