Java类和对象的初始化顺序

开发 后端
在Java中,类装载器把一个类装入Java虚拟机中,要经过三个步骤来完成:装载、链接和初始化,其中链接又可以分成校验、准备和解析三步,除了解析外,其它步骤是严格按照顺序完成的,各个步骤的主要工作如下:

本文摘录于http://blog.csdn.net/socoolfj/archive/2006/05/23/750425.aspx,并修改例子的代码,加上自己的见解。

类装载步骤

在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初始化块。

  1. public class Demo{   
  2.     //初始化static field,    
  3.     //其中= "initialization static field"又叫做static field initializer   
  4.     private static String str = "initialization static field";   
  5.    
  6.     //初始化块,又叫做static initializer,或 static initialization block   
  7.     static {   
  8.         System.out.println("This is static initializer");   
  9.     }   
  10. }  

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),则对它的引用不会引起定义它的类的初始化。

为了帮助理解***两点,请试试看下面的例子:

Initialization类

  1. public class Initialization {   
  2.    
  3.     static {   
  4.         System.out.println("Initialization Main class");   
  5.     }   
  6.    
  7.     public static void main(String[] args)  {   
  8.         System.out.println(Sub.y);   
  9.         System.out.println(Sub.x);   
  10.         System.out.println(Sub.z);   
  11.     }   
  12. }  

Sub类

  1. public class Sub extends Super {   
  2.     public static final int y = 2005;   
  3.     public static int z;   
  4.        
  5.     static {   
  6.         System.out.println("Initialization Sub");   
  7.     }   
  8. }  

Super类

  1. public class Super {   
  2.     public static int x = 2006;   
  3.        
  4.     static {   
  5.         System.out.println("Initialization Super");   
  6.     }   
  7. }  

输入结果

Initialization Main class 
2005 
Initialization Super 
2006 
Initialization Sub 
0

从这个结果可以看到,

  1. static块在类中会先执行;(实际上是先加载static成员变量,然后是static代码块)
  2. static 的final变量不会引起类的初始化;
  3. 子类Sub引用父类Super里面的变量,就会引起父类的初始化,但不会引起子类的初始化;
  4. static的成员变量也有默认值。

2. 对象的创建(creation of new class instances)

看例子来说明:

InitializationOrder类

  1. public class InitializationOrder {   
  2.     public static void main(String[] args) {   
  3.         SubClass sb = new SubClass();   
  4.     }   
  5. }  

SuperClass类

  1. public class SuperClass{   
  2.     static {   
  3.         System.out.println("SuperClass static");   
  4.     }   
  5.    
  6.     SuperClass(String str){   
  7.         System.out.println(str);   
  8.     }   
  9. }  

Interface类

  1. interface Interface{   
  2.     static SuperClass su = new SuperClass("Interface new SuperClass");   
  3. }  

SubClass类

  1. public class SubClass extends SuperClass implements Interface{   
  2.    
  3.     static {   
  4.         System.out.println("SubClass static");   
  5.     }   
  6.    
  7.     private SuperClass su = new SuperClass("initialization variable");   
  8.    
  9.     SubClass() {   
  10.         super("super");   
  11.         new SuperClass("new SuperClass");   
  12.     }   
  13. }  

输出结果

SuperClass static 
SubClass static 
super 
initialization variable 
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来初始化成员变量。(这里是第二次初始化成员变量)

(6) 按照书写顺序,执行构造函数的其余部分。

*******************

总结:

从类的初始化和对象的创建步骤,可以知道,一个类是先初始化static的变量和static句块,然后在分配该类以及父类的成员变量的内存空间,赋予默认值,然后开始调用构造函数。而子类和父类之间,则先初始化和创建父类,然后在初始化和创建子类的。

原文链接:http://android.blog.51cto.com/268543/384500

【编辑推荐】

  1. Think in Java之构造器的真正调用顺序
  2. 用Java编写你自己的简单HTTP服务器
  3. 拯救Java程序猿的神器:Play Framework
  4. 有可能挑战Java优势的四种技术
  5. Think in Java之斐波那契数列

 

责任编辑:林师授 来源: Icansoft的博客
相关推荐

2012-05-23 12:46:53

JavaJava类

2013-03-04 11:10:03

JavaJVM

2022-01-04 19:33:03

Java构造器调用

2010-07-28 10:22:33

FlexApplica

2009-07-03 16:21:33

Java的声明和初始化Java

2011-07-22 17:46:43

java

2011-06-17 15:29:44

C#对象初始化器集合初始化器

2012-04-09 13:43:12

Java

2011-03-23 15:02:55

ListenerFilterServlet

2009-07-31 17:51:27

C#对象初始化

2009-12-16 14:04:04

Ruby对象初始化

2024-03-12 07:44:53

JVM双亲委托机制类加载器

2020-11-02 07:02:10

加载链接初始化

2021-04-01 10:01:55

JavaStringJava基础

2009-08-27 10:31:39

C#对象初始化器

2011-06-09 14:13:06

C++JAVA缺省初始化

2012-03-13 13:38:42

Java

2024-03-08 08:26:25

类的加载Class文件Java

2019-11-04 13:50:36

Java数组编程语言

2009-08-24 18:01:45

C#3.0新特性
点赞
收藏

51CTO技术栈公众号