Java中的初始化

1、Java中类的加载:

每个类的编译代码都存在于他自己的独立文件中,该文件只有在需要使用程序代码的时候才会被加载。
i)何时会触发类加载?
类的代码在初次使用时才加载。
a)发生于创建类的第一个对象;
b)当访问static域或static方法时;
ii)加载类会发生的动作是什么?
所有的static对象和static代码段都会在加载时依程序中的顺序(即,定义类时的书写顺序)而依次初始化,静态初始化只在Class对象首次加载的时候儿进行一次。

2、对象(没有继承关系,即:没有基类的对象(除Object))的初始化(以名为Dog的类为例)

a)首次创建类型为Dog的对象(即使没有显式地使用static关键字,构造器上也是静态方法)时,或者Dog类的静态方法/静态域首次被访问时,Java解释器必须查找类路径,以定位Dog.class文件;
b)然后载入Dog.class,有关静态初始化的所有动作都会执行;
c)当使用new Dog()创建对象时,首先在堆上为Dog对象分配足够的存储空间;
d)这块儿存储空间会被清零(设为二进制零值),即将Dog对象中的所有基本类型数据都设置为了默认值(数字型和Charater为0,boolean为false),引用则被设置为null;
e)执行所有出现于字段定义处的初始化动作(与有继承关系的类不同,由于没有基类则不需要去调用基类的构造器,相当于到达递归的出口);
f)执行构造器的正文部分。

3、继承与初始化

了解包括继承在内的初始化全过程。结合程序示例来进行说明。

例程:

import static net.mindview.util.Print.*;

class Insect {
  private int i = 9;
  protected int j;
  Insect() {
    print("i = " + i + ", j = " + j);
    j = 39;
  }
  private static int x1 =
    printInit("static Insect.x1 initialized");
  static int printInit(String s) {
    print(s);
    return 47;
  }
}

public class Beetle extends Insect {
  private int k = printInit("Beetle.k initialized");
  public Beetle() {
    print("k = " + k);
    print("j = " + j);
  }
  private static int x2 =
    printInit("static Beetle.x2 initialized");
  public static void main(String[] args) {
    print("Beetle constructor");
    Beetle b = new Beetle();
  }
}
/* Output:
static Insect.x1 initialized
static Beetle.x2 initialized
Beetle constructor
i = 9, j = 0
Beetle.k initialized
k = 47
j = 39
*///:~

1)试图访问Beetle.main()(一个static方法),于是加载器开始启动并找出Beetle类的编译代码(在名为Beetle.class的文件中);

2)在加载Beetle.class的过程中,编译器发现他有一个基类Insert,于是继续继续加载Insert.class(如果Insert类还有基类,那么第二个基类还会被继续加载,如此递归类推,直至根基类);
本例中:在加载Beetle.class的过程中注意到,Beetle继承了Insert且Insert为根基类(即:出口),所以继续加载Insert.class。

3)加载递归到了根基类,到达了递归的出口–根基类中的static初始化(本例为Insert)即会被执行,然后是下一个导出类的static初始化。(为什么时递归的形式?因为导出类的static初始化可能会依赖于基类成员能否被正确初始化);
本例中:
先对根基类Insert.class的static初始化,即:private static int x1 = printInit(“static Insect.x1 initialized”)。然后是对导出类Beetle的static初始化,即:private static int x2=printInit(“static Beetle.x2 initialized”);

4)至此必要的类已经加载完毕(具备了生成对象的条件);
本例中:具备了生成对象的条件后没有立即生成对象,而是执行main函数的print(“Beetle constructor”)代码(即:此时的main函数还没有进行对象的生成,先执行这部分生成对象之前的代码);

5)开始创建导出类对象,首先在堆中给对象分配足够的空间,并且将这部分对象空间设为二进制零值(即:基本数据类型设为默认值,对象引用设为null);
本例中:new Beetle()则在堆上为Beetle对象创建足够的空间,并将Beetle的k域初始化为0,即:private int k=0。

6)基类的构造器会被调用(不论你创不创建改基类对象,都会调用。由于基类的一个子对象会嵌入到导出类,可以在导出类中显式的调用基类的构造器,如果不显式的调用,编译器会默认加上)。如果基类还有基类,依次递归(递归从第6步到7步)直到递归出口(根基类为出口),到达出后口后执行第8步;
本例中:
调用基类的构造器Insert(),且该类为根基类即为递归出口。在堆空间为Insert对象分配足够的存储空间;并且清零,即:private int i = 0;执行构载器中的剩余部分类容,即;print(“i = ” + i + “, j = ” + j);j = 39;

7)基类构造器完成之后,实例变量安其次序被初始化。
本例中:Insert基类构造完毕之后,给Beetle对象的实例变量初始化,即:k = printInit(“Beetle.k initialized”);

8)构造器的其余部分被执行。
本例中:
执行构造器中剩余代码:print(“k = ” + k);print(“j = ” + j);

4、具有组合、继承及多态的复杂类的构建顺序(例子的初始化过程可参照3的例子)

a)调用基类构造器。这个步骤会不断的反复递归下去,首先是构造这种层次结构的根,然后是下一导出类,
直到最低层的导出类;
b)按照声明顺序调用成员的初始化方法(例如:private int i = 1;);
c)调用导出类的构造器主题;

例程

//: polymorphism/Sandwich.java
// Order of constructor calls.
package polymorphism;
import static net.mindview.util.Print.*;

class Meal {
  Meal() { print("Meal()"); }
}

class Bread {
  Bread() { print("Bread()"); }
}

class Cheese {
  Cheese() { print("Cheese()"); }
}

class Lettuce {
  Lettuce() { print("Lettuce()"); }
}

class Lunch extends Meal {
  Lunch() { print("Lunch()"); }
}

class PortableLunch extends Lunch {
  PortableLunch() { print("PortableLunch()");}
}

public class Sandwich extends PortableLunch {
  private Bread b = new Bread();
  private Cheese c = new Cheese();
  private Lettuce l = new Lettuce();
  public Sandwich() { print("Sandwich()"); }
  public static void main(String[] args) {
    new Sandwich();
  }
} 
/* Output:
Meal()
Lunch()
PortableLunch()
Bread()
Cheese()
Lettuce()
Sandwich()
*///:~

步骤:
1)new Sandwich(),编译器执行面触发加载器去加载Sandwich.class;
2)编译器注意到Sandwich有基类PortableLunch,继续加载PortableLunch.class;PortableLunch有基类Lunch,继续加载Lunch.class;Lunch有基类Meal,继续加载Meal.class,至此Meal为根基类,到达递归出口;
3)应该可以进行静态部分的初始化,但是本例中没有静态成员或者静态块,所以不进行;
4)依次调用基类的构造器,递归出口为根基类器。所以构造器调用顺序为:Meal->Lunch->PortableLunch;
5)然后进行对象中非Static域的初始化,执行所有出现于字段定义处的初始化动作。即完成这些动作:b = new Bread(); c = new Cheese();e l = new Lettuce();
6)之后执行导出类构造器中剩余部分,即:print(“Sandwich()”)。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值