public class StaticInitSequence {
//-------------------Static fields-------------------
private static int staticIntVar = 10;
private static int staticComputeIntVar = (int)(Math.random() * 10);
private static String staticStrVar = "Static field init(before)";
private static Object staticRefVar = new Object();
static {
staticIntVar = 20;
staticStrVar = "Static block init(before)";
staticAfterIntVar = 40;
staticAfterStrVar = "Static block init(after)";
}
private static int staticAfterIntVar = 30;
private static String staticAfterStrVar = "Static field init(after)";
//---------------------Instance fields----------------
private int fieldIntVar = 100;
private int fieldComputeIntVar = (int)(Math.random() * 100);
private String fieldStrVar = "Instance field init(before)";
public StaticInitSequence() {
fieldIntVar = 200;
fieldStrVar = "Constructor field init(before)";
fieldAfterIntVar = 400;
fieldAfterStrVar = "Constructor field init(after)";
}
private int fieldAfterIntVar = 300;
private String fieldAfterStrVar = "Instance field init(after)";
public void print() {
System.out.println("----------------Static Fields------------");
System.out.println("staticIntVar: " + staticIntVar);
System.out.println("staticComputeIntVar: " + staticComputeIntVar);
System.out.println("staticStrVar: " + staticStrVar);
System.out.println("staticRefVar: " + staticRefVar);
System.out.println("staticAfterIntVar: " + staticAfterIntVar);
System.out.println("staticAfterStrVar: " + staticAfterStrVar);
System.out.println("-----------------Instance Fields---------");
System.out.println("fieldIntVar : " + fieldIntVar);
System.out.println("fieldComputeIntVar : " + fieldComputeIntVar);
System.out.println("fieldStrVar : " + fieldStrVar);
System.out.println("fieldAfterIntVar : " + fieldAfterIntVar);
System.out.println("fieldAfterStrVar : " + fieldAfterStrVar);
}
}
如果我们调用以上类的print()方法(new StaticInitSequence().print()),会有什么样的结果呢?
我自认为,直接对一个字段初始化是编译器提供支持的一种编程方式,这种编程方式可以提高代码的可读性,因为用户可以直接知道一个字段的初始值,而不用到构造函数或者静态语句块里面去找。在Java中,实际编译后的二进制文件中,所有的字段初始化语句都放在了初始化函数中(类(静态)初始化函数(<clinit>)或者实例初始化(构造函数/<init>)函数)。因此在我的逻辑思维中,在源代码中,初始化函数应该可以改变字段初始化中的值,这样还就可以在字段初始化中提供一个初始值,而在初始化函数中根据需要改变它。然而另我感到意外的是Java中只有实例初始化机制是这样实现的,而静态字段初始化中没有实现这种机制(在C#中不管实例初始化和静态初始化都实现了这种机制),静态字段初始化的顺序是完全根据源代码中定义顺序来初始化的;从耦合的角度,这就是一个顺序耦合的典型。不知道为什么Java要这样实现,是否它有其他方面的问题的考虑?亦或是Java设计者或者Java编译器设计者的一个失误?不管怎么样,用sun的javac编译出来的以上程序的运行结果如下:
----------------Static Fields------------
staticIntVar: 20
staticComputeIntVar: 7
staticStrVar: Static block init(before)
staticRefVar: java.lang.Object@14318bb
staticAfterIntVar: 30
staticAfterStrVar: Static field init(after)
-----------------Instance Fields---------
fieldIntVar : 200
fieldComputeIntVar : 8
fieldStrVar : Constructor field init(before)
fieldAfterIntVar : 400
fieldAfterStrVar : Constructor field init(after)
问题解释:
从以上程序生成的二进制代码就可以很好的解释以上的结果:
<clinit>:
//staticIntVar = 10
0 bipush 10
2 putstatic org.levin.insidejvm.miscs.staticinit.StaticInitSequence.staticIntVar : int [22]
// staticComputeIntVar = (int)(Math.random() * 10)
5 invokestatic java.lang.Math.random() : double [24]
8 ldc2_w <Double 10.0> [30]
11 dmul
12 d2i
13 putstatic org.levin.insidejvm.miscs.staticinit.StaticInitSequence.staticComputeIntVar : int [32]
//staticStrVar = “Static field init(before)”
16 ldc <String "Static field init(before)"> [34]
18 putstatic org.levin.insidejvm.miscs.staticinit.StaticInitSequence.staticStrVar : java.lang.String [36]
//staticRefVar = new Object();
21 new java.lang.Object [3]
24 dup
25 invokespecial java.lang.Object() [38]
28 putstatic org.levin.insidejvm.miscs.staticinit.StaticInitSequence.staticRefVar : java.lang.Object [41]
//staticIntVar = 20
31 bipush 20
33 putstatic org.levin.insidejvm.miscs.staticinit.StaticInitSequence.staticIntVar : int [22]
//staticStrVar = “Static block init(before)”
36 ldc <String "Static block init(before)"> [43]
38 putstatic org.levin.insidejvm.miscs.staticinit.StaticInitSequence.staticStrVar : java.lang.String [36]
//staticAfterIntVar = 40
41 bipush 40
43 putstatic org.levin.insidejvm.miscs.staticinit.StaticInitSequence.staticAfterIntVar : int [45]
//staticAfterStr = “Statci block init(after)”
46 ldc <String "Static block init(after)"> [47]
48 putstatic org.levin.insidejvm.miscs.staticinit.StaticInitSequence.staticAfterStrVar : java.lang.String [49]
//staticAfterIntVar = 30
51 bipush 30
53 putstatic org.levin.insidejvm.miscs.staticinit.StaticInitSequence.staticAfterIntVar : int [45]
//staticAfterStrVar = “Static field init(after)”
56 ldc <String "Static field init(after)"> [51]
58 putstatic org.levin.insidejvm.miscs.staticinit.StaticInitSequence.staticAfterStrVar : java.lang.String [49]
61 return
<init>:
//invoke base constructor
0 aload_0 [this]
1 invokespecial java.lang.Object() [38]
4 aload_0 [this]
//fieldIntVar = 100
5 bipush 100
7 putfield org.levin.insidejvm.miscs.staticinit.StaticInitSequence.fieldIntVar : int [55]
//fieldComputeIntVar = (int)(Math.random() * 100)
10 aload_0 [this]
11 invokestatic java.lang.Math.random() : double [24]
14 ldc2_w <Double 100.0> [57]
17 dmul
18 d2i
19 putfield org.levin.insidejvm.miscs.staticinit.StaticInitSequence.fieldComputeIntVar : int [59]
//fieldStrVar = “Instance field init(before)”
22 aload_0 [this]
23 ldc <String "Instance field init(before)"> [61]
25 putfield org.levin.insidejvm.miscs.staticinit.StaticInitSequence.fieldStrVar : java.lang.String [63]
//fieldAfterIntVar = 300
28 aload_0 [this]
29 sipush 300
32 putfield org.levin.insidejvm.miscs.staticinit.StaticInitSequence.fieldAfterIntVar : int [65]
//fieldAfterStrVar = “Instance field init(after)”
35 aload_0 [this]
36 ldc <String "Instance field init(after)"> [67]
38 putfield org.levin.insidejvm.miscs.staticinit.StaticInitSequence.fieldAfterStrVar : java.lang.String [69]
//fieldIntVar = 200
41 aload_0 [this]
42 sipush 200
45 putfield org.levin.insidejvm.miscs.staticinit.StaticInitSequence.fieldIntVar : int [55]
//fieldStrVar = “Constructor field init(before)”
48 aload_0 [this]
49 ldc <String "Constructor field init(before)"> [71]
51 putfield org.levin.insidejvm.miscs.staticinit.StaticInitSequence.fieldStrVar : java.lang.String [63]
//fieldAfterIntVar = 400
54 aload_0 [this]
55 sipush 400
58 putfield org.levin.insidejvm.miscs.staticinit.StaticInitSequence.fieldAfterIntVar : int [65]
//fieldAfterStrVar = “Constructor field init(after)”
61 aload_0 [this]
62 ldc <String "Constructor field init(after)"> [73]
64 putfield org.levin.insidejvm.miscs.staticinit.StaticInitSequence.fieldAfterStrVar : java.lang.String [69]
67 return
问题延伸
在这里,细心的人可能还会想到另外一个问题,如果StaticInitSequence类还有父类,并且父类中同同时有静态成员初始化,静态语句块初始化,实例成员初始化,构造函数初始化,那会这样的顺序会是怎么样的呢?在Java中,保证父类的初始化要早于子类的初始化,因而如果有父类存在的话,一定是先父类初始化做好以后才做子类的初始化(这一点和C#又有略微的不同,在C#中,子类的字段初始化语句要早于父类的初始化语句和构造函数),并且是先静态初始化再实例初始化。
相关推荐
在上面的示例代码中,我们可以看到,类变量和实例变量的初始化顺序是按照定义的顺序进行的。同时,我们还可以看到,静态变量的初始化顺序是按照定义的顺序,并且只在第一次访问时初始化。 在 Java 中,static ...
然而,在涉及到类继承时,一个重要的问题是类实例化时的初始化顺序。本篇文章将围绕“类继承的初始化顺序”这一主题展开,详细解析初始化过程中涉及的关键概念和技术细节。 ### 类继承的初始化顺序概述 在面向对象...
总之,深入理解Java的ClassLoader机制和类变量初始化顺序是提升Java编程技能的重要步骤。通过学习这些知识点,开发者可以更好地优化代码、设计更健壮的系统,并解决与类加载和初始化相关的复杂问题。
以下是对Java程序初始化顺序的详细说明: 1. **类加载阶段**: - **加载**:当Java虚拟机(JVM)首次遇到一个类的引用时,会通过类加载器进行加载。加载过程包括找到类的.class文件,读取其字节码,并转化为内存中...
这个顺序表明,无论类之间的继承关系如何,初始化顺序始终是:静态变量和静态初始化块先于非静态成员。在创建对象时,父类的初始化先于子类。这是Java语言规范所规定的,确保在子类访问父类的静态或非静态成员时,...
3. **静态成员变量与实例成员变量的初始化顺序** ```java public class TestOrder { // 静态成员变量初始化 public static TestA a = new TestA(); // 静态初始化块 static { System.out.println("静态初始...
ANSWER: 静态变量和静态初始化块的初始化顺序是最高的,因此 Father 类中的静态变量和静态初始化块将首先被初始化,接着是 Son 类中的静态变量和静态初始化块。然后是变量和初始化块,最后是构造器。 以下是一个...
java 静态_非静态 字段_方法_代码块 子类父类构造_初始化顺序! 三个class 让你清清楚楚 第一个class java代码如下: package initialOrder; class Parent { // 静态变量 public static String p_StaticField...
了解静态变量的初始化顺序对于理解和避免潜在的编程陷阱至关重要。 ### 静态变量初始化的基本规则: 1. **默认初始化**:当类被加载时,静态变量首先会被赋予其数据类型的默认值。例如,`int`类型的静态变量会被...
在C++中,初始化顺序遵循以下规则: 1. 静态成员变量:无论它们在哪里定义,静态成员变量都按照声明的顺序初始化。 2. 非静态数据成员:在构造函数初始化列表中,成员变量按照它们在类声明中的顺序被初始化。即使...
在Java编程语言中,理解成员变量的初始化顺序对于正确地设计和实现类结构至关重要。通过本实验,我们将深入了解成员变量的初始化顺序,以及如何通过实际代码示例来验证这些概念。 #### 实验步骤解析 ##### 步骤一...
例如,在 `Bowl` 类中,`b6` 和 `b9` 是两个静态变量,它们的初始化顺序是按照它们在类中的定义顺序进行的。 在 `main` 函数中,我们可以看到创建了两个 `Cupboard` 对象,每个对象的创建都会触发静态变量的初始化...
总之,类的初始化顺序是:静态成员 -> 静态初始化块 -> 非静态成员 -> 非静态初始化块 -> 构造器。这个顺序同样适用于继承关系,只是会先初始化父类的部分,再初始化子类的部分。掌握这一知识能帮助程序员更好地设计...
Java语言中的类初始化顺序是面试中常见的问题,尤其对于Java程序员和工程师来说,理解这一概念至关重要。本篇文章将深入解析类初始化的顺序以及在继承情况下的表现。 首先,我们需要明确类初始化顺序的基本规则: ...
Java对象的初始化顺序是一个关键的编程概念,它涉及到类加载、静态初始化、实例初始化等多个步骤。下面我们将详细探讨这些步骤。 首先,当程序运行并创建一个新的对象时,JVM(Java虚拟机)会按照特定的顺序来初始...
这里我们将深入探讨两种类型的初始化块:静态初始化块(Static Initializer Block)和对象初始化块(Instance Initializer Block)。这两种初始化块在创建类实例或加载类时分别扮演着不同的角色。 **静态初始化块**...
这意味着在静态方法内部,无法使用`this`或`super`关键字,也无法直接访问类的实例变量和实例方法,原因在于静态方法并不依赖于任何具体实例的存在。静态方法通常用于执行与类本身相关、而不依赖于任何特定实例状态...
执行上述代码,输出结果会清晰地展示出JAVA在处理继承时的初始化顺序,证实了静态变量和静态初始化块优先于实例变量和构造器初始化的规则。 #### 总结 理解和掌握JAVA中静态变量与实例变量的初始化顺序是JAVA开发...
特别是当涉及到静态域(静态变量)、非静态域(实例变量)、静态块、非静态块以及构造函数时,明确它们的初始化顺序有助于避免潜在的编程错误。 根据题目提供的内容,我们将重点讨论这些概念以及它们之间的相互关系...
final 修饰的变量有三种:静态变量、实例变量和局部变量,分别表示三种类型的常量。final 变量定义的时候,可以先声明,而不给初值,这中变量也称为 final 空白,无论什么情况,编译器都确保空白 final 在使用之前...