1 类加载的时机
本章主要内容: 虚拟机如何加载这些Class文件,Class文件中的信息进入到虚拟机中后会发生什么变化。

类加载的时机¶
类加载机制:Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这个过程被称为虚拟机的类加载机制。
主动引用:有且仅有 6 种,必须立即对类进行“初始化”(而加载、验证,准备自然需要在此之前开始)
除此之外,所有引用类型的方式都不会触发初始化,称为被动引用。
例子1:
package ClassLoading;
class SuperClass{
static {
System.out.println("SuperClass init!");
}
public static int value = 123;
}
class SubClass extends SuperClass{
static {
System.out.println("SubClass init!");
}
}
public class NotInitialization {
public static void main(String[] args) {
System.out.println(SubClass.value);
}
}
//输出:
//SuperClass init!
//123
对于静态字段,只有直接定义这个字段的类才会被初始化,因此通过其子类来引用父类中定义的静态字段,只会触发父类的初始化而不会触发子类的初始化。
例子2:
package ClassLoading;
class SuperClass{
static {
System.out.println("SuperClass init!");
}
public static int value = 123;
}
class SubClass extends SuperClass{
static {
System.out.println("SubClass init!");
}
}
public class NotInitialization {
public static void main(String[] args) {
//使用数组定义来引用类,不会触发此类的初始化
//程序不会输出任何数据
SuperClass[] sca = new SuperClass[10];
}
}
Java语言中对数组的访问要比C/C++相对安全,很大程度上就是因为这个类包装了数组元素的访问,而C/C++中则是直接翻译为对数组指针的移动。在Java语言里,当检查到发生数组越界时会抛出java.lang.ArrayIndexOutOfBoundsException异常,避免了直接造成非法内存访问。
例子3:
package ClassLoading;
class ConstClass{
static {
System.out.println("ConstClass init!");
}
public static final String HeLLOWORLD = "hello world";
}
public class NotInitialization {
public static void main(String[] args) {
//常量在编译阶段会存入调用类的常量池中,本质上没有直接引用到定义常量的类,因此不会触发 定义常量的类的初始化
System.out.println(ConstClass.HeLLOWORLD);
}
}
//输出: hello world
虽然在Java源码中确实引用了ConstClass类的常量HELLOWORLD,但其实在编译阶段通过常量传播优化,已经将此常量的值"helloworld"直接存储在NotInitialization类的常量池中,以后NotInitialization对常量ConstClass.HELLOWORLD的引用,实际都转化为NotInitialization类对自身常量池的引用了。也就是说,实际上NotInitialization的class文件之中并没有ConstClass类的符号引用入口,这两个类在编译成Class文件后就已不存在任何联系了。