01-类加载过程

2020-02-07  本文已影响0人  AcientFish

类加载过程

类加载可以分为加载、连接、初始化3个部分

加载

加载过程是指查找并加载类的二进制数据,加载class文件的方式可以有以下几种

类加载的最终产品就是位于内存中的Class对象。Class对象封装了类在方法区内的数据结构并提供了能够访问方法区内这些数据结构的接口。

Java提供了两种类加载器
Java虚拟机自带的类加载器
  1. Bootstrap ClassLoader(由C++编写)
  2. Extension ClassLoader(纯Java的加载器)
  3. App/System ClassLoader(纯Java的加载器)
用户自定义的类加载器

1.java.lang.ClassLoader的子类

连接

连接就是对已经读入内存的类的二进制数据合并到虚拟机的运行时环境中去,连接又分为3个步骤,分别是

初始化

初始化是指为类的静态变量设置正确的初始值,即在代码中真正设置的值。

类初始化步骤:

类的初始化时机

类的初始化时机与类的使用方式密切相关

Java程序对类的两种使用方式

其中主动使用会引发上面的初始化操作。Java虚拟机要求每个类或接口在其被首次主动使用时才初始化类或接口。

主动使用某个类或接口的七种情况
  1. 创建实例(new Object)
  2. 调用类的静态方法
  3. 对类的静态属性get或set
  4. 反射(Class.forName,需要注意的是在Class.forName时有一个initialize参数可以控制是否需要初始化,默认为true。)
  5. 初始化一个类的子类
  6. Java虚拟机启动时被标记为启动类的类
  7. JDK1.7提供的对动态语言支持的三个句柄对应的类没初始化时

除了以上7种情况外的都是对类的被动使用,被动使用是不会导致对类的初始化的。

另外访问类的静态属性时有一种特殊情况不会造成类的主动使用,示例代码如下

public class Sample{
  public static final int a = 1;
  public static final int b = new Random(3).nextInt(3);
}

public class Main {
  public static void main(String args[]){
    // 这里直接访问Sample的静态属性a,但并不会初始化Sample类,因为属性a是final的,因此在编译期编译器会将a的值放入Main类的常量池中,在运行时并不会去引用Sample类
    System.out.println(Sample.a);
    // 这里对Sample的静态属性b的直接访问会造成Sample的初始化,因为虽然b也是被final修饰的,但b的值需要在运行时才能确定,因此还是需要对Sample类进行初始化操作
    System.out.println(Sample.b);
  }
}

调用ClassLoader的loadClass并不是对类的主动使用,因此不会进行初始化。

接口的初始化时机

上文已经说过,当Java虚拟机初始化一个类时,要求其所有父类都已经被初始化,但此规则并不适用于接口

针对接口来说遵循如下两种规则

因此,一个父接口并不会因为其字接口或实现类的初始化而初始化,只有当程序首次主动使用特定接口的静态变量时才会对其进行初始化。

上一篇下一篇

猜你喜欢

热点阅读