Java类和对象的初始化顺序

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介:
本文摘录于 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.     } 
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.     } 
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.     } 
Super类
 
  1. public class Super { 
  2.     public static int x = 2006
  3.      
  4.     static { 
  5.         System.out.println("Initialization Super"); 
  6.     } 
输入结果
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.     } 
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.     } 
Interface类
 
  1. interface Interface{ 
  2.     static SuperClass su = new SuperClass("Interface new SuperClass"); 
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.     } 
输出结果
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句块,然后在分配该类以及父类的成员变量的内存空间,赋予默认值,然后开始调用构造函数。而子类和父类之间,则先初始化和创建父类,然后在初始化和创建子类的。
 
因此当我们引用类的static变量时,是没有分配该类以及父类的成员变量的内存空间的。


本文转自 Icansoft 51CTO博客,原文链接: 
http://blog.51cto.com/android/384500
相关文章
|
1天前
|
Java
Java快速入门之类、对象、方法
本文简要介绍了Java快速入门中的类、对象和方法。首先,解释了类和对象的概念,类是对象的抽象,对象是类的具体实例。接着,阐述了类的定义和组成,包括属性和行为,并展示了如何创建和使用对象。然后,讨论了成员变量与局部变量的区别,强调了封装的重要性,通过`private`关键字隐藏数据并提供`get/set`方法访问。最后,介绍了构造方法的定义和重载,以及标准类的制作规范,帮助初学者理解如何构建完整的Java类。
|
3天前
|
存储 Java C++
Java数组:静态初始化与动态初始化详解
本文介绍了Java中数组的定义、特点及初始化方式。
29 12
|
15天前
|
Java
java代码优化:判断内聚到实体对象中和构造上下文对象传递参数
通过两个常见的java后端实例场景探讨代码优化,代码不是优化出来的,而是设计出来的,我们永远不可能有专门的时间去做代码优化,优化和设计在平时
29 15
|
2月前
|
Java 开发者
在 Java 中,一个类可以实现多个接口吗?
这是 Java 面向对象编程的一个重要特性,它提供了极大的灵活性和扩展性。
176 57
|
30天前
|
JSON Java Apache
Java基础-常用API-Object类
继承是面向对象编程的重要特性,允许从已有类派生新类。Java采用单继承机制,默认所有类继承自Object类。Object类提供了多个常用方法,如`clone()`用于复制对象,`equals()`判断对象是否相等,`hashCode()`计算哈希码,`toString()`返回对象的字符串表示,`wait()`、`notify()`和`notifyAll()`用于线程同步,`finalize()`在对象被垃圾回收时调用。掌握这些方法有助于更好地理解和使用Java中的对象行为。
|
2月前
|
存储 缓存 安全
java 中操作字符串都有哪些类,它们之间有什么区别
Java中操作字符串的类主要有String、StringBuilder和StringBuffer。String是不可变的,每次操作都会生成新对象;StringBuilder和StringBuffer都是可变的,但StringBuilder是非线程安全的,而StringBuffer是线程安全的,因此性能略低。
81 8
|
2月前
|
安全 Java 编译器
Java对象一定分配在堆上吗?
本文探讨了Java对象的内存分配问题,重点介绍了JVM的逃逸分析技术及其优化策略。逃逸分析能判断对象是否会在作用域外被访问,从而决定对象是否需要分配到堆上。文章详细讲解了栈上分配、标量替换和同步消除三种优化策略,并通过示例代码说明了这些技术的应用场景。
Java对象一定分配在堆上吗?
|
2月前
|
存储 安全 Java
java.util的Collections类
Collections 类位于 java.util 包下,提供了许多有用的对象和方法,来简化java中集合的创建、处理和多线程管理。掌握此类将非常有助于提升开发效率和维护代码的简洁性,同时对于程序的稳定性和安全性有大有帮助。
92 17
|
2月前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
2月前
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
149 4

热门文章

最新文章