`

[#0x0023] class loading: further discussion involving steps of instance creation

    博客分类:
  • Java
阅读更多

  本文对[#0x0001][#0x0008][#0x000B]做统一归纳。

 

  一个类在能够被程序使用之前,必须经历三个准备工作(以下统称为类的执行):

  -> 1. loading

  -> 2. linking

      --> 2.1 verification

      --> 2.2 preparation

      --> 2.3 resolution (optional)

  -> 3. inintialization

 

  在[#0x0008][#0x000B]中,我们使用的loading (加载),其实是统指了以上三个步骤。

 

  loading指从.class文件中读取类的binary representation(即类的Class对象)的过程。

  verfication过程验证binary representation的结构是否正确。

  preparation过程为类的static field申请空间并赋默认值,同时为类的一些内部数据结构(如方法列表) 申请空间。

  resolution过程分析类中的引用。resolution过程是一个optional的过程,在resolution过程中可以有不同的loading策略,比如说,在resolve class A的时候,发现class A中有一个class B的引用,此时可以立即加载class B,也可以do nothing。

  initialization过程执行static initializer和initializer for static field (i.e. static variable initializer)。如:

private static int i = 5; //static variable initializer

//static initializer
static
{
	......
}

以下属于initialization阶段执行的代码:

private int i = StaticFunction(); //虽然涉及到了static方法,不过field不是static,不能算是static variable initialzer

//虽然是对static field初始化,但这个initializer本身不是static,依旧不能算是static initializer
{
	StaticField = xxx;
	......
} 

 

  由于loading和linking过程是implementation-dependent,且不方便追踪和查看,所以暂不讨论loading和linking的触发条件。以下着重讨论initialization。

  initialization三原则:

  -> 1. 触发原则:以下三种场景执行之前会触发initialization

      --> 创建类的实例(constrcutor or Class.newInstance())

      --> 调用类的static方法(包括constructor)

      --> final的static field is used or assigned

  -> 2. 父类原则:子类initialization之前,其direct父类必须initialization,and do it recursively. (p.s. 类实现的接口无需initialization,类实现的接口的父接口亦无需如此)

  -> 3. 引发原则:如果子类initialization引发了父类的initialization,而此时父类还没有loading和linking,则父类的loading和linking也会被引发(p.s. 我觉得子类的initialization同样可以引发子类的loading和linking,如果loading和linking还没有执行的话)。

 

  这里需要着重强调的是:loading、linking和initialization都是类的行为(class behavior) (所以initialization执行的都是static),而实例的创建(constructor or Class.newInstance())则是对象行为(object behavior)。

 

  constructor执行的过程:

  -> 执行this() or super()

  -> 执行initializer和non-static variable initializer

  -> 执行constructor的余下部分

 

  回头看[#0x0008]的例子:

//@file Beetle.java

import static java.lang.System.out;

class Insect 
{
	private int i = 1;
	protected int j;
	private static int x1 = printInit("static Insect.x1 initialized");
	
	Insect() 
	{
		out.println("Insect constructor");
		out.println("i = " + i + ", j = " + j + ", x1 = " + x1);
		this.j = 2;
	}
	
	static int printInit(String s) 
	{
		out.println(s);

		return 3;
	}
}

public class Beetle extends Insect 
{
	private int k = printInit("Beetle.k initialized");
	private static int x2 = printInit("static Beetle.x2 initialized");
	
	public Beetle() 
	{
		out.println("Beetle constructor");
		out.println("j = " + j + ", k = " + k + ", x2 = " + x2);
	}
 
	public static void main(String[] args) 
	{
		Beetle b = new Beetle();
	}
}
//output:
/*
	static Insect.x1 initialized
	static Beetle.x2 initialized
	Insect constructor
	i = 1, j = 0, x1 = 3
	Beetle.k initialized
	Beetle constructor
	j = 2, k = 3, x2 = 3
*/

  -> 访问Insect的main方法,是个static,引发Beetle的loading、linking和initialization,initialization又引发Insect的loading、linking和initialization

      --> 执行Insect的initialization,private static int x1( = 3),打印"static Insect.x1 initialized"

      --> 执行Beetle的initialization,private static int x2( = 3),打印"static Beetle.x2 initialized"

  -> 进入main(),发现constructor

  -> 隐式调用super(),转到Insect的constructor

      --> Insect已经loading、linking和initialization了,直接执行non-static variable initializer,初始化private int i( = 1)和protected int j( = 0 by default)

      --> 执行Insect constructor的余下部分,打印"Insect constructor"和"i = 1, j = 0, x1 = 3",然后j = 2

  -> 执行Beetle的non-static variable initializer,初始化private int k( = 3),打印"Beetle.k initialized"

  -> 执行Beetle constructor的余下部分,打印"Beetle constructor"和"j = 2, k = 3, x2 = 3"

 

  回头看[#0x000B]的例子:

class Glyph
{
	void draw() 
	{ 
		System.out.println("Glyph.draw()");
	}
	
	Glyph()
	{
		System.out.println("Glyph constructor");
		draw();
	}
}	

class RoundGlyph extends Glyph 
{
	private int radius = 1;

	RoundGlyph(int r)
	{
		System.out.println("before assignment in constructor, radius = " + radius);
		radius = r;
		System.out.println("RoundGlyph constructor, radius = " + radius);
	}
	
	void draw()
	{
		System.out.println("RoundGlyph.draw(), radius = " + radius);
	}
}	

public class PolyConstructors
{
	public static void main(String[] args)
	{
		new RoundGlyph(5);
	}
}
//Output:
/*
	Glyph constructor
	RoundGlyph.draw(), radius = 0
	before assignment in constructor, radius = 1
	RoundGlyph constructor, radius = 5
*/

  -> 访问PolyConstructors的main方法,loading、linking PolyConstructors,进入main,发现RoundGlyph构造器

  -> 隐式调用super(),打印"Glyph constructor",执行RoundGlyph的draw()方法,打印"RoundGlyph.draw(), radius = 0" (此时还没有执行到RoundGlyph的non-static variable initializer)

  -> 执行RoundGlyph的non-static variable initializer,radius = 1

  -> 执行RoundGlyph构造器的余下部分,打印"before assignment in constructor, radius = 1",然后radius = 5,打印"RoundGlyph constructor, radius = 5"

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics