主动引用和被动引用

87 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 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