`

java 静态初始化,动态初始化,以及构造器执行的顺序

阅读更多

大家在去参加面试的时候,经常会遇到这样的考题:给你两个类的代码,它们之间是继承的关系,每个类里只有构造器方法和一些变量,构造器里可能还有一段代码对变量值进行了某种运算,另外还有一些将变量值输出到控制台的代码,然后让我们判断输出的结果。这实际上是在考查我们对于继承情况下类的初始化顺序的了解。

我们大家都知道,对于静态变量、静态初始化块、变量、初始化块、构造器,它们的初始化顺序以此是(静态变量、静态初始化块)>(变量、初始化块)>构造器。我们也可以通过下面的测试代码来验证这一点:

Java代码 复制代码
  1. public class InitialOrderTest {   
  2.   
  3.     // 静态变量   
  4.     public static String staticField = "静态变量";   
  5.     // 变量   
  6.     public String field = "变量";   
  7.   
  8.     // 静态初始化块   
  9.     static {   
  10.         System.out.println(staticField);   
  11.         System.out.println("静态初始化块");   
  12.     }   
  13.   
  14.     // 初始化块   
  15.     {   
  16.         System.out.println(field);   
  17.         System.out.println("初始化块");   
  18.     }   
  19.   
  20.     // 构造器   
  21.     public InitialOrderTest() {   
  22.         System.out.println("构造器");   
  23.     }   
  24.   
  25.     public static void main(String[] args) {   
  26.         new InitialOrderTest();   
  27.     }   
  28. }  
public class InitialOrderTest {

	// 静态变量
	public static String staticField = "静态变量";
	// 变量
	public String field = "变量";

	// 静态初始化块
	static {
		System.out.println(staticField);
		System.out.println("静态初始化块");
	}

	// 初始化块
	{
		System.out.println(field);
		System.out.println("初始化块");
	}

	// 构造器
	public InitialOrderTest() {
		System.out.println("构造器");
	}

	public static void main(String[] args) {
		new InitialOrderTest();
	}
}


运行以上代码,我们会得到如下的输出结果:

  1. 静态变量
  2. 静态初始化块
  3. 变量
  4. 初始化块
  5. 构造器


这与上文中说的完全符合。那么对于继承情况下又会怎样呢?我们仍然以一段测试代码来获取最终结果:

Java代码 复制代码
  1. class Parent {   
  2.     // 静态变量   
  3.     public static String p_StaticField = "父类--静态变量";   
  4.     // 变量   
  5.     public String p_Field = "父类--变量";   
  6.   
  7.     // 静态初始化块   
  8.     static {   
  9.         System.out.println(p_StaticField);   
  10.         System.out.println("父类--静态初始化块");   
  11.     }   
  12.   
  13.     // 初始化块   
  14.     {   
  15.         System.out.println(p_Field);   
  16.         System.out.println("父类--初始化块");   
  17.     }   
  18.   
  19.     // 构造器   
  20.     public Parent() {   
  21.         System.out.println("父类--构造器");   
  22.     }   
  23. }   
  24.   
  25. public class SubClass extends Parent {   
  26.     // 静态变量   
  27.     public static String s_StaticField = "子类--静态变量";   
  28.     // 变量   
  29.     public String s_Field = "子类--变量";   
  30.     // 静态初始化块   
  31.     static {   
  32.         System.out.println(s_StaticField);   
  33.         System.out.println("子类--静态初始化块");   
  34.     }   
  35.     // 初始化块   
  36.     {   
  37.         System.out.println(s_Field);   
  38.         System.out.println("子类--初始化块");   
  39.     }   
  40.   
  41.     // 构造器   
  42.     public SubClass() {   
  43.         System.out.println("子类--构造器");   
  44.     }   
  45.   
  46.     // 程序入口   
  47.     public static void main(String[] args) {   
  48.         new SubClass();   
  49.     }   
  50. }  
class Parent {
	// 静态变量
	public static String p_StaticField = "父类--静态变量";
	// 变量
	public String p_Field = "父类--变量";

	// 静态初始化块
	static {
		System.out.println(p_StaticField);
		System.out.println("父类--静态初始化块");
	}

	// 初始化块
	{
		System.out.println(p_Field);
		System.out.println("父类--初始化块");
	}

	// 构造器
	public Parent() {
		System.out.println("父类--构造器");
	}
}

public class SubClass extends Parent {
	// 静态变量
	public static String s_StaticField = "子类--静态变量";
	// 变量
	public String s_Field = "子类--变量";
	// 静态初始化块
	static {
		System.out.println(s_StaticField);
		System.out.println("子类--静态初始化块");
	}
	// 初始化块
	{
		System.out.println(s_Field);
		System.out.println("子类--初始化块");
	}

	// 构造器
	public SubClass() {
		System.out.println("子类--构造器");
	}

	// 程序入口
	public static void main(String[] args) {
		new SubClass();
	}
}


运行一下上面的代码,结果马上呈现在我们的眼前:

  1. 父类--静态变量
  2. 父类--静态初始化块
  3. 子类--静态变量
  4. 子类--静态初始化块
  5. 父类--变量
  6. 父类--初始化块
  7. 父类--构造器
  8. 子类--变量
  9. 子类--初始化块
  10. 子类--构造器


现在,结果已经不言自明了。大家可能会注意到一点,那就是,并不是父类完全初始化完毕后才进行子类的初始化,实际上子类的静态变量和静态初始化块的初始化是在父类的变量、初始化块和构造器初始化之前就完成了。

那么对于静态变量和静态初始化块之间、变量和初始化块之间的先后顺序又是怎样呢?是否静态变量总是先于静态初始化块,变量总是先于初始化块就被初始化了呢?实际上这取决于它们在类中出现的先后顺序。我们以静态变量和静态初始化块为例来进行说明。

同样,我们还是写一个类来进行测试:

Java代码 复制代码
  1. public class TestOrder {   
  2.     // 静态变量   
  3.     public static TestA a = new TestA();   
  4.        
  5.     // 静态初始化块   
  6.     static {   
  7.         System.out.println("静态初始化块");   
  8.     }   
  9.        
  10.     // 静态变量   
  11.     public static TestB b = new TestB();   
  12.   
  13.     public static void main(String[] args) {   
  14.         new TestOrder();   
  15.     }   
  16. }   
  17.   
  18. class TestA {   
  19.     public TestA() {   
  20.         System.out.println("Test--A");   
  21.     }   
  22. }   
  23.   
  24. class TestB {   
  25.     public TestB() {   
  26.         System.out.println("Test--B");   
  27.     }   
  28. }  
public class TestOrder {
	// 静态变量
	public static TestA a = new TestA();
	
	// 静态初始化块
	static {
		System.out.println("静态初始化块");
	}
	
	// 静态变量
	public static TestB b = new TestB();

	public static void main(String[] args) {
		new TestOrder();
	}
}

class TestA {
	public TestA() {
		System.out.println("Test--A");
	}
}

class TestB {
	public TestB() {
		System.out.println("Test--B");
	}
}


运行上面的代码,会得到如下的结果:

  1. Test--A
  2. 静态初始化块
  3. Test--B


大家可以随意改变变量a、变量b以及静态初始化块的前后位置,就会发现输出结果随着它们在类中出现的前后顺序而改变,这就说明静态变量和静态初始化块是依照他们在类中的定义顺序进行初始化的。同样,变量和初始化块也遵循这个规律。

了解了继承情况下类的初始化顺序之后,如何判断最终输出结果就迎刃而解了。

 

另外的一个例子:
class Egg2 {
 protected class Yolk {
  public Yolk() {
   System.out.println("Egg2.Yolk()");
  }

  public void f() {
   System.out.println("Egg2.Yolk.f()");
  }
 }

 private Yolk y = new Yolk();

 public Egg2() {
  System.out.println("New Egg2()");
 }

 public void insertYolk(Yolk yy) {
  y = yy;
 }

 public void g() {
  y.f();
 }
}

public class BigEgg2 extends Egg2 {
 private Yolk a = new Yolk();
 public class Yolk extends Egg2.Yolk {
  public Yolk() {
   System.out.println("BigEgg2.Yolk()");
  }

  public void f() {
   System.out.println("BigEgg2.Yolk.f()");
  }
 }

 public BigEgg2() {
  insertYolk(new Yolk());
 }

 public static void main(String[] args) {
  Egg2 e2 = new BigEgg2();
  e2.g();
 }
}

输出结果:

Egg2.Yolk()
New Egg2()
Egg2.Yolk()
BigEgg2.Yolk()
Egg2.Yolk()
BigEgg2.Yolk()
BigEgg2.Yolk.f()

分享到:
评论

相关推荐

    java 静态非静态 字段方法 子类父类构造_初始化顺序!

    System.out.println("父类--静态初始化块"); } // 初始化块 { System.out.println(p_Field); System.out.println("父类--初始化块"); } // 构造器 public Parent() { System.out.println(...

    java程序初始化顺序

    - 静态初始化块(static{}):如果类中存在静态初始化块,该块的代码会在类加载后的准备阶段执行,用于初始化静态变量或执行其他静态设置任务。 - 静态变量的赋值:接着,静态变量会被赋予它们的初始值,这可以是...

    java面试题-类的初始化顺序.doc

    1. 首先,执行父类的静态初始化块,打印出 "父类--静态变量" 和 "父类--静态初始化块"。 2. 接着,执行子类的静态初始化块,打印出 "子类--静态变量" 和 "子类--静态初始化块"。 3. 然后,创建子类实例,首先执行...

    Java类继承初始化顺序

    总之,Java类继承初始化顺序涉及到静态和非静态初始化块、构造器的调用以及方法的覆盖。理解这些概念对于编写健壮的、易于维护的Java代码至关重要。在实际编程中,应合理利用继承特性,同时注意避免不必要的复杂性和...

    java代码的初始化顺序demo

    静态初始化块(也称为静态初始化器)是在类被加载并初始化时执行的,且只执行一次。它用于初始化类级别的静态变量。在`init`目录下的文件可能包含了不同静态初始化的示例。 3. **实例化阶段**: 当我们创建一个新...

    学习java静态数据初始化.doc

    ### Java静态数据初始化详解 #### 一、Java静态初始化...通过本篇文章的学习,我们了解了Java中静态初始化的基本概念、执行过程以及如何正确地在程序中使用静态初始化。这对于理解和优化Java应用程序具有重要意义。

    java 构造器的调用

    在Java编程语言中,构造器...了解和掌握构造器的调用、初始化、默认构造器、继承中的构造器调用以及构造器重载等概念,是编写高质量Java代码的基础。通过熟练运用这些知识,可以更有效地管理对象的生命周期和状态。

    JAVA类的初始化顺序文.pdf

    总之,类的初始化顺序是:静态成员 -> 静态初始化块 -> 非静态成员 -> 非静态初始化块 -> 构造器。这个顺序同样适用于继承关系,只是会先初始化父类的部分,再初始化子类的部分。掌握这一知识能帮助程序员更好地设计...

    Java初始化块Java初始化块.doc

    这个过程是自顶向下的,从`java.lang.Object`开始,逐级执行每个类的静态初始化块。 总结起来,Java初始化块是Java中用于对象和类初始化的重要工具,它们在不同场景下提供了一种灵活的方式来控制对象和类的状态。...

    JAVA面试题解惑系列——类的初始化顺序

    首先,需要了解Java类初始化的基本规则,即在类的静态变量、静态初始化块、变量、初始化块、构造器这五个部分中,它们的初始化顺序依次是:静态变量、静态初始化块、变量、初始化块、构造器。这个顺序在单个类中是...

    Java变量初始化

    2. 非静态初始化代码块中对实例变量指定初值; 3. 构造器中对实例变量指定初值。 虽然程序在三个地方都可对实例变量指定初始值,但(1)、(2)处的赋值操作最终会被提取到构造器中执行。且(1)、(2)的执行顺序...

    C++和Java初始化顺序的比较代码

    2. 静态初始化块:当类第一次被加载时执行,按块的顺序执行。 3. 实例字段:在构造器内部,按照声明的顺序初始化。 4. 实例初始化块:在构造器执行之前,按块的顺序执行。 例如: ```java public class MyClass { ...

    java 静态块实例块构造器调用

    Java编程语言中,类的初始化过程涉及到静态块(static block)、实例块(instance block)以及构造器(constructor)。这些块的执行顺序对于理解和编写复杂的Java程序至关重要,特别是在涉及继承关系时。下面我们将...

    JAVA面试题解惑系列类的初始化顺序JAVA程序员JAVA工程师面试必看.pdf

    4. **子类的非静态部分**:最后执行子类的非静态变量初始化和非静态初始化块,以及子类的构造器。 下面是一个包含继承的例子: ```java class Parent { public static String p_StaticField = "父类--静态变量"; ...

    详解Java的初始化与清理

    3. 接着,执行当前类的非静态初始化,包括当前类的构造器。 4. 最后,执行实例初始化块(如果存在)。 以下是一个例子,展示了这个顺序: ```java class Insect { private int i = 9; protected int j; private...

    Java初始化顺序1

    Java 中的初始化顺序是按照定义的顺序进行的,而静态变量的初始化顺序则是按照定义的顺序,并且只在第一次访问时初始化。 Java 初始化顺序的知识点总结: * 类变量(静态变量)和实例变量(对象变量)的区别 * ...

    深入理解java构造器机理

    在 Java 编程语言中,构造器是一种特殊的方法,用于初始化对象的创建。它是 Java 类中最重要的一个概念。下面将深入讨论构造器的机理、执行顺序、作用及与其他概念的区别。 一、构造器的机理 构造器是一种特殊的...

    对象初始化流程梳理对象初始化流程梳理

    Java中的对象初始化流程是编程实践中一个非常重要的概念,它涉及到类加载、静态初始化块、实例初始化块、构造器等多个方面。下面将详细解释这个过程。 首先,对象初始化流程的起点是程序的入口点,即`main`方法。当...

    探究java的ClassLoader及类变量初始化顺序

    需要注意的是,静态初始化块只在类被加载时执行一次,而实例初始化块(构造函数)则会在每次创建类的实例时执行。静态变量的初始化是在类加载时完成的,而实例变量的初始化则在对象创建时进行。 在实际开发中,对...

Global site tag (gtag.js) - Google Analytics