携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第23天,点击查看活动详情
1.被动引用和主动引用
在java虚拟机规范中,严格规定了,只有对类进行主动引用,才会触发其初始化方法。而除此之外的引用方式称之为被动引用,不会触发类的初始化方法。
1.1主动引用
虚拟机规范规定只有如下四种情况才能触发主动引用:
1.1.1.遇到new、getstatic、setstatic、invokestatic 4条指令时,如果类没有初始化,则需要触发其初始化(final修饰的常量除外)。
- (1).使用new关键字实例化对象
package com.dhb.classload;
public class NewClass {
static {
System.out.println("NewClass init ...");
}
}
class Init1{
public static void main(String[] args) {
new NewClass();
}
}
//输出结果:
NewClass init ...
- (2).读取类的静态成员变量
package com.dhb.classload;
public class StaticAttributeClass {
public static int value = 10;
public static void staticMethod() {
}
static {
System.out.println("StaticAttributeClass init ...");
}
}
class Init2{
public static void main(String[] args) {
//1.读取静态变量
int value = StaticAttributeClass.value;
}
}
//输出结果
StaticAttributeClass init ...
- (3).设置类的静态成员变量
class Init2{
public static void main(String[] args) {
StaticAttributeClass.value = 5
}
}
//输出结果
StaticAttributeClass init ...
- (4).调用静态方法
class Init2{
public static void main(String[] args) {
StaticAttributeClass.staticMethod();
}
}
//输出结果
StaticAttributeClass init ...
1.1.2.使用java.lang.reflenct包的方法对类进行放射调用,如果没有进行初始化,则需要触发其初始化。
package com.dhb.classload;
public class ReflectClass {
static {
System.out.println("ReflectClass init ...");
}
}
class Init3{
public static void main(String[] args) {
try {
Class clazz = Class.forName("com.dhb.classload.ReflectClass");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
//输出结果
ReflectClass init ..
1.1.3.当一个类初始化的时候,如果其父类还没有初始化,则需要先对其父类进行初始化。
package com.dhb.classload;
public class SuperClass {
static {
System.out.println("SuperClass init ...");
}
public static int value = 10;
}
class SubClass extends SuperClass {
static {
System.out.println("SubClass init ...");
}
}
class Init4 {
public static void main(String[] args) {
new SubClass();
}
}
//输出结果
SuperClass init ...
SubClass init ...
1.1.4.当虚拟机启动时,用户需要指定一个执行的主类,虚拟机会首先初始化这个主类
package com.dhb.classload;
public class MainClass {
static {
System.out.println("MainClass init ...");
}
public static void main(String[] args) {
System.out.println("main begin ...");
}
}
//输出结果
MainClass init ...
main begin ...
1.2被动引用
主动引用之外的引用情况都称之为被动引用,这些引用不会进行初始化。
1.2.1.通过子类引用父类的静态字段,不会导致子类初始化
package com.dhb.classload;
public class SuperClass {
static {
System.out.println("SuperClass init ...");
}
public static int value = 10;
}
class SubClass extends SuperClass {
static {
System.out.println("SubClass init ...");
}
}
class Init4 {
public static void main(String[] args) {
int value = SubClass.value;
}
}
//输出结果
SuperClass init ...
1.2.2.通过数组定义来引用,不会触发此类的初始化
package com.dhb.classload;
public class ArrayClass {
static {
System.out.println("ArrayClass init ...");
}
}
class Init5{
public static void main(String[] args) {
ArrayClass[] arrays = new ArrayClass[10];
}
}
//输出结果为空
1.2.3.常量在编译阶段会存入调用类的常量池中,本质没有直接引用到定义的常量类中,因此不会触发定义的常量类初始化
package com.dhb.classload;
public class ConstClass {
static {
System.out.println("ConstClass init ...");
}
public static final int value = 10;
}
class Init6{
public static void main(String[] args) {
int value = ConstClass.value;
}
}
//输出结果为空
1.3练习题
如下类的输出:
package com.dhb.classload;
public class Singleton {
private static Singleton instance = new Singleton();
public static int x = 0;
public static int y;
private Singleton () {
x ++;
y ++;
}
public static Singleton getInstance() {
return instance;
}
public static void main(String[] args) {
Singleton singleton = getInstance();
System.out.println(x);
System.out.println(y);
}
}
上述类的执行结果为:
输出结果竟然是 x为0 y为1 !!! 其实理解了类的加载过程也就不难理解,其过程如下:
-
(1).执行链接过程,初始化所有的类变量: instance -> null x -> 0 y -> 0
-
(2).执行初始化过程: new Singleton() 调用构造方法 之后 x -> 1 y -> 1 再执行 x = 0 赋值 最终 x -> 0
y -> 1