我们知道,在初始化一个类时,如果它的父类没有进行初始化,那么JVM就会触发其父类的初始化动作。但是,当我们引用类的时候,可不一定会触发其初始化,这种引用类的方式称为
被动引用。
看下面的例子:
public class NotInit {
public static void main(String[] args) {
System.out.println(Child.val);
// System.out.println(Father.str);
Father[] father = new Father[4];
System.out.println(Constant.HELLO);
}
}
class Father {
static {
System.out.println("Father init!");
}
public static int val = 100;
// public static String str = "string";
}
class Child extends Father {
static {
System.out.println("Child init!");
}
}
class Constant {
static {
System.out.println("Constant init");
}
public static final String HELLO = "HELLO";
}
见证奇迹的时刻到了!!!程序输出为:
Father init!
100
HELLO
这不科学啊!!!代码中调用了
System.out.println(Child.val);
可是没有竟然没有对Child类进行初始化。这是因为val为静态字段,只有直接定义这个字段的类才会被初始化,故通过子类来引用父类静态字段val,只会触发Father类的初始化,而不会触发Child类的初始化,so 就没有Child类的事。
再来,代码中虽然有
Father[] father = new Father[4];
可是Father类居然没有初始化,这也是情理之中的。这句代码仅仅定义了一个Father类型的一维数组,数组里面什么都没有放!就好比我买了一个可以容纳100L的容器,而且是只能用来装浓硫酸的容器,但是我现在还没把浓硫酸放入容器,所以容器中什么也没有,即是没有初始化。
看官接着往下瞧:
System.out.println(Constant.HELLO);
Constant类没有直接父类,可为何还是没能初始化Constant类。大家也看见了,HELLO是一个常量。常量和一般的变量不一样,因为HELLO是常量,所以Constant类在编译阶段通过常量优化传播,把HELLO所代表的"hello"存储到了NotInit类的常量池中。说人话就是,以后NotInit类对常量Constant.HELLO的引用实际都转化为NotInit类对自身常量池的引用了。在编译结束后,Constant类和NotInit类就劳燕分飞各自走了。
总结是个好习惯!本文描述了类加载过程中的三种被动引用,这些情况下都不会触发相应类的初始化:
一、 通过子类引用父类的静态字段,不会导致子类初始化
二、 通过数组定义来引用类,不会触发该类的初始化
三、 常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的那个类,所以也不会触发定义常量的类的初始化
分享到:
相关推荐
Java虚拟机JVM类加载初始化是Java程序运行过程中的关键环节,它负责将类的字节码文件加载到内存中并进行相应的处理,以便程序能够正确执行。在Java中,类加载器(Classloader)扮演着核心角色。下面将详细讨论类加载...
除了以上五种情况外,其他所有引用类的方式都不会触发初始化,这种引用方式被称为被动引用。 值得注意的是,接口的加载过程与类加载过程稍有不同。接口在初始化时,并不要求其父接口全部都完成了初始化,只有在真正...
#### Java加载初始化的关键概念 Java程序在运行过程中涉及几个关键的概念,包括类加载、连接以及初始化等阶段。 1. **类加载**(Load):这是类生命周期的第一个阶段,主要任务是将编译好的字节码文件(`.class`...
除了上述需要初始化的情况外,还有一些“被动引用”的情况,即虽然使用到了某个类或其成员,但是不会触发类的初始化,比如: - 通过子类引用父类的静态字段。 - 通过数组定义来引用类。 - 引用某个类的常量。 #### ...
被动使用类,如通过子类、反射等不会触发初始化。 JVM的启动通常始于一个特定的初始类,这个类由虚拟机实现指定。JVM会首先加载这个初始类,链接其类信息,然后初始化并调用`public static void main(String[])`...
被动使用类不会触发初始化。 b) 初始化的具体行为:执行类变量初始化语句和静态初始化块,为类变量赋予正确的初始值。 4. 卸载 类的卸载在JVM中相对罕见,通常发生在JVM运行空间不足,垃圾收集器回收无用的类时。...
Java虚拟机规范规定了5种触发初始化的主动引用情况: 1. 使用new关键字实例化对象。 2. 访问或修改类的静态字段(不包括final修饰的静态常量)。 3. 调用类的静态方法。 4. 初始化主类(包含main方法的类)。 5. ...
被动使用不会触发初始化,例如通过Class.forName()获取类的信息。 接下来,我们要讨论的是**双亲委托模型**。这是Java类加载器遵循的一种策略,它保证了类的唯一性。当一个类加载器接收到加载类的请求时,它首先会...
只有当类被首次主动使用时,才会触发初始化,被动使用不会导致初始化。 6. **双亲委派模型**:Java的类加载机制遵循双亲委派模型,即子类加载器先尝试将加载请求委托给父类加载器,只有父类加载器无法加载时,子类...
被动使用类(如通过子类、反射等)不会触发初始化。 在实际应用中,类加载器有多种,包括Bootstrap ClassLoader(引导类加载器)、Extension ClassLoader(扩展类加载器)和AppClassLoader(应用程序类加载器)。...
主动使用包括:创建类的实例、访问类的静态变量、调用类的静态方法、反射方式加载类、子类的初始化会触发父类加载等。被动使用则是在JVM运行过程中,由于JVM或JDK内部需要而进行的加载,如JNI查找类、JDK工具类的...
类加载器在Java虚拟机(JVM)中扮演着至关重要的角色,它是程序运行的基础,负责将类的二进制数据加载到内存中并进行初始化。类加载的过程分为三个主要阶段:加载、连接和初始化。 1. **加载**:在这个阶段,类加载...
这五种情况被称为主动引用,除此之外,所有类的方法都不会触发初始化,称为被动引用。 在Java中,有些情况不会主动初始化,例如: * 子类引用父类的静态常量,实际上是父类的指令,而不是子类的指令,因此不会主动...
以下四种情况会触发初始化: - 使用`new`关键字实例化对象。 - 访问或修改类的静态字段(非final的)。 - 调用类的静态方法(`static`关键字声明的)。 - 使用反射API。 - JVM启动时指定的主类(包含`main`方法的类...
被动使用类不会触发加载,例如通过反射调用类的静态方法。 6. **类的卸载** 与加载相比,类的卸载在Java中并不常见。JVM通常不会卸载已经加载的类,除非对应的ClassLoader被卸载,且该类没有其他ClassLoader引用。...
被动使用类(如通过子类引用父类的静态变量)不会触发初始化。 4. **自定义类加载器**: 开发者可以自定义类加载器实现特定的加载策略,例如从网络、数据库或其他非标准位置加载类。自定义类加载器需要继承java....
被动引用,如通过子类引用父类的静态变量,不会触发初始化。 7. **卸载**:满足特定条件后,如类的所有实例被回收,加载类的类加载器被回收,以及没有其他引用指向该类时,类可能会被卸载。 类加载器的层次结构...
在Java虚拟机(JVM)中,类的初始化和加载器的父委托机制是至关重要的概念,它们决定了类的生命周期和类加载的顺序。本文将深入探讨这两个主题,并结合命名空间等深层次的知识点进行讲解。 首先,我们来看类的初始...
需要注意的是,除了以上六种主动用法,其他对类的被动用法不会触发初始化。例如,仅通过子类引用父类的静态字段不会导致父类的初始化。 这个过程保证了类的安全性和正确性,避免了非法代码的执行。理解并掌握Java类...