在Java中,类装载器把一个类装入Java虚拟机中,要经过三个步骤来完成:装载、链接和初始化,其中链接又可以分成校验、准备和解析三步,除了解析外,其它步骤是严格按照顺序完成的,各个步骤的主要工作如下:
链接:执行下面的校验、准备和解析步骤,其中解析步骤是可以选择的;
初始化:激活类的静态变量的初始化Java代码和静态Java代码块。
其中 初始化(initialization)包含两部分:
1.类的初始化(initialization class & interface)
2.对象的创建(creation of new class instances)。
因为类的初始化其实是类加载(loading of classes)的最后一步,所以很多书中把它归结为“对象的创建”的第一步。其实只是看问题的角度不同而已。为了更清楚的理解,这里还是分开来。
因为类的加载肯定是第一步的,所以类的初始化在前。大体的初始化顺序是:
类初始化 -> 子类构造函数 -> 父类构造函数 -> 实例化成员变量 -> 继续执行子类构造函数的语句
1. 类的初始化(Initialization classes and interfaces) (a)初始化类(initialization of class),是指初始化static field 和执行static初始化块。
- public class Demo{
-
-
- private static String str = "initialization static field";
-
-
- static {
- System.out.println("This is static initializer");
- }
- }
btw,有些书上提到static initializer 和 static field initializer 的概念,与之对应的还有 instance initializer 和 instance variable initializer。例子中的注释已经解释了其含义。
(b)初始化接口(initialization of interface),是指初始化定义在该interface中的field。
1.
initialization classes 时,该class的superclass 将首先被初始化,但其实现的interface则不会。 initialization classes 时,该class的superclass,以及superlcass的superclass 会首先被递归地初始化,一直到java.lang.Object为止。
但initialiazation interface的时候,却不需如此,只会初始化该interface本身。
2. 对于由引用类变量(class field)所引发的初始化,只会初始化真正定义该field的class。 3. 如果一个static field是编译时常量(compile-time constant),则对它的引用不会引起定义它的类的初始化。 - public class Initialization {
-
- static {
- System.out.println("Initialization Main class");
- }
-
- public static void main(String[] args) {
- System.out.println(Sub.y);
- System.out.println(Sub.x);
- System.out.println(Sub.z);
- }
- }
- public class Sub extends Super {
- public static final int y = 2005;
- public static int z;
-
- static {
- System.out.println("Initialization Sub");
- }
- }
- public class Super {
- public static int x = 2006;
-
- static {
- System.out.println("Initialization Super");
- }
- }
Initialization Main class
1. static块在类中会先执行;(实际上是先加载static成员变量,然后是static代码块) 2. static 的final变量不会引起类的初始化; 3. 子类Sub引用父类Super里面的变量,就会引起父类的初始化,但不会引起子类的初始化; 2. 对象的创建(creation of new class instances) - public class InitializationOrder {
- public static void main(String[] args) {
- SubClass sb = new SubClass();
- }
- }
- public class SuperClass{
- static {
- System.out.println("SuperClass static");
- }
-
- SuperClass(String str){
- System.out.println(str);
- }
- }
- interface Interface{
- static SuperClass su = new SuperClass("Interface new SuperClass");
- }
- public class SubClass extends SuperClass implements Interface{
-
- static {
- System.out.println("SubClass static");
- }
-
- private SuperClass su = new SuperClass("initialization variable");
-
- SubClass() {
- super("super");
- new SuperClass("new SuperClass");
- }
- }
1) Java虚拟机要执行InitializationOrder类中的static 方法main(),这引起了类的初始化。开始初始化InitializationOrder类。具体的步骤略去不说。
2) InitializationOrder类初始化完毕后,开始执行main()方法。语句SubClass sb = new SubClass()将创建一个SubClass对象。加载类SubClass后对其进行类初始化,因为Subclass有一个父类SuperClass,所以先初始化SuperClass类。于是看到输出“SuperClass static”。
3) SuperClass类初始化完毕后,开始初始化SubClass类,输出“SubClass static”。
4) 至此,类的加载工作全部完成。开始进入创建SubClass的对象过程。先为SubClass类和其父类SuperClass类分配内存空间,
这时Super su 被赋值为null。 5) 执行构造函数SubClass(),执行super(), 调用父类的构造函数,输出“super”。
6) 初始化SubClass类的成员变量su,输出“initialization variable”。
7) 继续执行构造函数的剩余部分,执行new SuperClass("new SuperClass"),输出“new SuperClass”,
这时Super su 被赋值新建对象的引用。
8) 而SubClass虽然实现了接口Interface,但是初始化它的时候并不会引起接口的初始化,所以接口Interface中的static SuperClass su = new SuperClass("Interface new SuperClass")自始至终都没有被执行到。
(1) 所有的成员变量—包括该类,及它的父类中的成员变量--被分配内存空间,并赋予默认值。(这里是第一次初始化成员变量)
(2) 为所调用的构造函数初始化其参数变量。(如果有参数)
(3) 如果在构造函数中用this 调用了同类中的其他构造函数,则按照步骤(2)~(6)去处理被调用到的构造函数。
(4) 如果在构造函数中用super调用了其父类的构造函数,则按照步骤(2)~(6)去处理被调用到的父类构造函数。
(5) 按照书写顺序,执行instance initializer 和 instance variable initializer来初始化成员变量。(这里是第二次初始化成员变量)
从类的初始化和对象的创建步骤,可以知道,一个类是先初始化static的变量和static句块,然后在分配该类以及父类的成员变量的内存空间,赋予默认值,然后开始调用构造函数。而子类和父类之间,则先初始化和创建父类,然后在初始化和创建子类的。 因此当我们引用类的static变量时,是没有分配该类以及父类的成员变量的内存空间的。
本文转自 Icansoft 51CTO博客,原文链接:
http://blog.51cto.com/android/384500