在Java代码中,有些类看上去初始化了,但其实没有。例如定义一定长度某一类型的数组,看上去数组中所有的元素已经被初始化,实际上一个都没有。
对于类的初始化,虚拟机规范严格规定了只有对该类进行主动引用时,才会触发。而除此之外的所有引用方式称之为对类的被动引用,不会触发类的初始化。
虚拟机规范严格地规定了有且仅有四种情况是对类的主动引用,即必须立即对类进行初始化。四种情况如下:
1.遇到new、getstatic、putstatic或invokestatic这4条字节码指令时,如果类没有进行初始化,则需要先触发其初始化。
(1)使用new关键字实例化对象
/** * @author Perlin.Yao */ public class NewClass { static { System.out.println("NewClass init!"); } } /** * @author Perlin.Yao */ public class Initialization1 { public static void main(String[] args) { new NewClass(); } } // 输出结果 NewClass init!
(2)读取类的静态成员变量
/** * @author Perlin.Yao */ public class StaticAttributeClass { public static int value = 1; public static void staticMethod() { //System.out.println("staticMethod invoked"); } static { System.out.println("StaticAttributeClass init!"); } } /** * @author Perlin.Yao */ public class Initialization2 { public static void main(String[] args) { // 1.读取静态变量 int x = StaticAttributeClass.value; } } // 输出结果 StaticAttributeClass init!
(3)设置类的静态成员变量
/** * @author Perlin.Yao */ public class Initialization2 { public static void main(String[] args) { // 2.设置静态变量 StaticAttributeClass.value = 2; } } // 输出结果 StaticAttributeClass init!
(4)调用类的静态方法
/** * @author Perlin.Yao */ public class Initialization2 { public static void main(String[] args) { // 3.调用静态方法 StaticAttributeClass.staticMethod(); } } // 输出结果 StaticAttributeClass init!
2.使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行初始化,则需要先触发其初始化。
/** * @author Perlin.Yao */ public class ReflectClass { static { System.out.println("ReflectClass init!"); } } /** * @author Perlin.Yao */ public class Initialization3 { public static void main(String[] args) throws Exception { Class classB = Class.forName("jvm.init.ReflectClass"); } } // 输出结果 ReflectClass init!
3.当一个类初始化的时候,如果发现其父类还没有初始化,则需要先对其父类进行初始化。
/** * @author Perlin.Yao */ public class SuperClass { static { System.out.println("SuperClass init!"); } public static int value = 123; } /** * @author Perlin.Yao */ public class SubClass extends SuperClass { static { System.out.println("SubClass init!"); } } /** * @author Perlin.Yao */ public class Initialization4 { public static void main(String[] args) { new SubClass(); } } // 输出结果 SuperClass init! SubClass init!
4.当虚拟机启动时,用户需要指定一个要执行的主类,虚拟机会先初始化这个主类
其实就是public static void main(String[] args)所在的那个类
下面来列举几个对类被动引用的例子:
1.通过子类引用父类的的静态字段,不会导致子类初始化
/** * @author Perlin.Yao */ public class SuperClass { static { System.out.println("SuperClass init!"); } public static int value = 123; } /** * @author Perlin.Yao */ public class SubClass extends SuperClass { static { System.out.println("SubClass init!"); } } /** * @author Perlin.Yao */ public class NotInitialization1 { public static void main(String[] args) { int x = SubClass.value; } } // 输出结果 SuperClass init!
2.通过数组定义来引用类,不会触发此类的初始化
/** * @author Perlin.Yao */ public class NotInitialization2 { public static void main(String[] args) { SuperClass[] sca = new SuperClass[10]; } } // 无任何输出
3.常量在编译阶段会存入调用类的常量池中,本质上没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化
/** * @author Perlin.Yao */ public class ConstClass { static { System.out.println("ConstClass init!"); } public static final int value = 123; } /** * @author Perlin.Yao */ public class NotInitialization3 { public static void main(String[] args) { int x = ConstClass.value; } } // 无任何输出结果
参考书籍:《深入Java虚拟机》
《深入理解Java虚拟机》
相关推荐
浅谈Java中对类的主动引用和被动引用 Java 中的类引用可以分为两种:主动引用和被动引用。理解这两种引用机制对于 Java 程序的正确执行和优化至关重要。 一、主动引用 主动引用是指在 Java 程序中明确地使用某个...
Java虚拟机JVM类加载初始化是Java程序运行过程中的关键环节,它负责将类的字节码文件加载到内存中并进行相应的处理,以便程序能够正确执行。在Java中,类加载器(Classloader)扮演着核心角色。下面将详细讨论类加载...
Java虚拟机规范规定了5种触发初始化的主动引用情况: 1. 使用new关键字实例化对象。 2. 访问或修改类的静态字段(不包括final修饰的静态常量)。 3. 调用类的静态方法。 4. 初始化主类(包含main方法的类)。 5. ...
被动引用,如反射访问或子类对父类的引用,则不会触发类的加载。 #### 三、自定义ClassLoader的意义 虽然JVM自带的ClassLoader功能强大,但在特定情况下,自定义ClassLoader有其必要性: 1. **安全性增强**:在...
本文主要探讨JVM的类加载机制,包括类加载、连接、初始化等关键过程,以及类的主动使用和被动使用的情况。 首先,我们要理解**类加载**的作用。JVM的类加载器(ClassLoader)负责将编译后的`.class`文件加载到内存...
Java类加载机制是Java运行时环境中的核心组成部分,它负责将编译后的字节码(.class文件)加载到Java虚拟机(JVM)中,使得程序能够执行。这一过程涉及多个步骤,包括加载、验证、准备、解析和初始化。理解类加载...
例如,你可以实现热部署,动态加载网络上的类,或者对类进行加密解密后再加载等。 除了基础的类加载,还有**类的全限定名**、**类加载器的层次结构**、**类的生命周期**等知识点。理解这些概念对于进行JVM调优、...
Java 类加载过程是Java虚拟机(JVM)在运行时动态加载类的机制,它涉及类的生命周期中的几个关键步骤,包括装载、连接、初始化和卸载。下面将详细阐述这些过程。 1. 装载 装载阶段是类加载的起始点,主要分为三个...
Java程序对类的使用分为主动使用和被动使用。只有在六种特定情况下,类才会被初始化:创建类实例、访问或修改静态变量、调用静态方法、反射操作、初始化子类以及JVM启动时指定的启动类。其他情况被视为被动使用,...
需要注意的是,除了以上六种主动用法,其他对类的被动用法不会触发初始化。例如,仅通过子类引用父类的静态字段不会导致父类的初始化。 这个过程保证了类的安全性和正确性,避免了非法代码的执行。理解并掌握Java类...
相反,“被动使用”是指不触发类加载的一些操作,如通过数组定义来引用类(不会触发此类的初始化),或者仅存在对类的引用但并未实际使用。 了解这些基本概念后,我们还需要知道类的生命周期,包括加载、验证、准备...
4. **使用**:类的使用包括主动引用和被动引用。主动引用会导致类实例的创建,而被动引用则不会。例如,创建类数组仅引用类而不实例化,引用类的常量也不会导致初始化。 5. **卸载**:类的卸载发生在所有条件满足时...
Java程序对类的使用方式主要分为两大类:主动使用和被动使用。主动使用包括创建类的实例、访问或修改静态变量、调用静态方法、反射操作、子类初始化以及作为启动类。这些情形下,JVM会触发类的初始化过程。相反,...
在提供的资源中,"JAVA 虚拟机运行机制-培训资料交流(刘晓阳).ppt"很可能包含了关于类加载机制的详细讲解和实例分析,可以帮助深入理解这一主题。而"test.zip"可能包含了一些示例代码或测试用例,用于实践和演示类...
在Java面试中,JVM相关知识是常见的话题,尤其是类的生命周期和加载机制。 类的生命周期主要包括七个阶段:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化...
初始化阶段是类生命周期中最关键的部分,它执行类的静态初始化语句(static块)和对静态变量的赋值操作。只有当类被首次主动使用时(如创建类实例、调用静态方法或访问静态字段),才会触发初始化。如果类只被被动...
Java程序对类的使用方式可以分为**主动使用**和**被动使用**两大类: - **主动使用**: - 创建类的实例。 - 访问或修改静态变量。 - 调用静态方法。 - 反射(例如使用`Class.forName()`)。 - 初始化一个类的...
Java对类的使用分为两类:主动使用和被动使用。只有当类被主动使用时,JVM才会进行初始化。主动使用包括: - 创建类实例 - 访问类的静态变量或对其赋值 - 调用类的静态方法 - 使用反射加载类 - 初始化类的子类 - ...
初始化阶段是在类或接口被首次主动引用时触发的。以下四种情况会触发初始化: - 使用`new`关键字实例化对象。 - 访问或修改类的静态字段(非final的)。 - 调用类的静态方法(`static`关键字声明的)。 - 使用反射...