97

我有两个班级 Parent 和 Child

public class Parent {    
    public Parent() {
        System.out.println("Parent Constructor");
    }    
    static {
        System.out.println("Parent static block");    
    }    
    {
        System.out.println("Parent initialisation  block");
    }
}

public class Child extends Parent {    
    {
        System.out.println("Child initialisation block");
    }
    static {
        System.out.println("Child static block");
    }

    public Child() {
        System.out.println("Child Constructor");
    }    
    public static void main(String[] args) {
        new Child();    
    }
}

上述代码的输出将是

Parent static block
Child static block
Parent initialization  block
Parent Constructor
Child initialization block
Child Constructor

为什么Java按那个顺序执行代码?确定执行顺序的规则是什么?

4

11 回答 11

69

我在视觉上学习,所以这是顺序的视觉表示,作为SSCCE

public class Example {

    static {
        step(1);
    }

    public static int step_2 = step(2);
    public int step_8 = step(8);

    public Example(int unused) {
        super();
        step(10);
    }

    {
        step(9);
    }

    // Just for demonstration purposes:
    public static int step(int step) {
        System.out.println("Step " + step);
        return step;
    }
}
public class ExampleSubclass extends Example {

    {
        step(11);
    }

    public static int step_3 = step(3);
    public int step_12 = step(12);

    static {
        step(4);
    }

    public ExampleSubclass(int unused) {
        super(step(7));
        step(13);
    }

    public static void main(String[] args) {
        step(5);
        new ExampleSubclass(step(6));
        step(14);
    }
}

这打印:

Step 1
Step 2
Step 3
Step 4
Step 5
Step 6
Step 7
Step 8
Step 9
Step 10
Step 11
Step 12
Step 13
Step 14

请记住,static零件的顺序很重要;Example回头看看's staticstuff 和's的顺序的区别ExampleSubclass

另请注意,无论顺序如何,实例初始化块总是super()在构造函数中的调用之后立即执行(即使该调用是隐含/省略的)。但是,初始化块和字段初始化器之间的顺序确实很重要。

于 2016-01-25T20:08:23.887 回答
60

有几条规则在起作用

  • 静态块总是在创建对象之前运行,这就是为什么您会看到来自父静态块和子静态块的打印消息
  • 现在,当您调用子类(子类)的构造函数时,此构造函数super();会在执行它自己的构造函数之前隐式调用。初始化块甚至在构造函数调用之前就开始发挥作用,所以这就是它首先被调用的原因。因此,现在您的父级已创建,程序可以继续创建将经历相同过程的子类。

说明:

  1. 父类的静态块首先执行,因为它首先加载,并且在加载类时调用静态块。
于 2013-10-24T08:59:39.523 回答
9

首先 - 仅运行子类(注释 extend 子句)以查看简单流程。

第二 - 转到Java 中的静态块与初始化块?并阅读那里接受的答案。

编辑:

  1. 执行以 SIC 方式发生 - 静态、(非静态)初始化器和构造器。
  2. (非静态)初始化器被复制到每个构造函数中 - 在顶部!(因此第 3/4/5/6 行)
  3. 在初始化类之前,必须初始化其直接超类 - http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4(因此首先出现父静态块) .
于 2013-10-24T09:09:44.913 回答
9
  • 静态初始化块在类加载时执行。
  • 在类层次结构中,静态初始化块的执行顺序将从顶级类开始。
  • 在一个类中,静态块的执行顺序是从上到下。
  • 无论静态块在类中的何处存在,上述规则都适用。

(在您的代码中,父静态块将首先执行,然后是子类静态块。)

  • 实例初始化块将在调用 super() 之后执行;在构造函数中。
    • 总是超级();是默认构造函数中的第一条语句。

在您创建子对象时的代码中:

  • Child 类的默认构造函数被执行。
  • 它将调用 super(); 构造函数。
  • 然后执行超类构造函数。
  • Parent 类将执行其 super(); 称呼。
  • 之后,父类中的实例初始化块被执行。(从上到下)。
  • 然后执行构造函数中的代码(如果有的话)。
  • 然后它将返回到 Child 类并执行 Child 类实例的 init 块。
  • 最后,子构造函数中的代码被执行(如果存在)。
于 2018-06-21T10:29:38.353 回答
5

java中的静态块在main方法之前执行。如果我们在 java 类中声明一个静态块,它会在类加载时执行。这是用静态变量初始化的。它主要用于 JDBC。每次加载类时都会执行java中的静态块。这也称为静态初始化块。java中的静态块在类加载到内存时初始化,这意味着当JVM读取字节码时。初始化可以是任何东西;它可以是变量初始化或应该由该类的所有对象共享的任何其他内容。静态块是用大括号 { } 括起来的普通代码块,前面有 static 关键字。

所以首先执行静态块。

实例初始化块:每次创建类的实例时运行。

所以在创建类的实例时执行下一个初始化块。

然后构造函数执行

于 2013-10-24T09:05:22.930 回答
4

只是想分享我的发现。我在另一个线程上读到一个答案,即在不正确的静态字段之前首先执行静态块。这取决于哪个先出现,静态字段或静态块。看看下面的代码。它将尝试正确看待事物。

  1. JVM 查找具有 public static void main(String args[]) 的类,以便它可以加载该类。
  2. 然后它初始化这个类的静态字段(如果它们在静态块之前)。这些字段可以调用此类或其他类的静态方法。如果他们调用此类的静态方法,则该方法将被提供。如果他们调用另一个类的静态方法,则该类的静态字段或块(取决于哪个先出现)首先被初始化,然后提供此方法调用。
  3. 然后,它移动到静态块。
  4. 它回到主要方法。

    class TestLab {
    static int method(String a) {
        System.out.println("in static method of TestLab" + " Coming from " + a);
        System.out.println("b is " + b);
        return 6;
    }
    
    static int a = method("Line 11");
    static int b = 7;
    
    TestLab() {
        System.out.println("Inside test lab constructor");
    }
    
    static {
        System.out.println("In static block of TestLab");
    }
    
    }
    
    public class Test1 {
    public static void main(String[] args) {
        System.out.println("inside main method of Test 1");
        int a = TestLab.method("Line 26");
    }
    
    // static Test ref=new Test();
    Test1() {
        System.out.println("Default Constructor of Test1");
    }
    
    {
        System.out.println("In instance block of Test1");
    }
    static int d = TestLab.method("Line 37");
    static int e = methodOfTest1();
    static {
        System.out.println("In Static Block of Test1");
    }
    
    static int methodOfTest1() {
        System.out.println("inside static method:mehtodOfTest1()");
        return 3;
    }
    }
    

这是输出:

in static method of TestLab Coming from Line 11
b is 0
In static block of TestLab
in static method of TestLab Coming from Line 37
b is 7
inside static method:mehtodOfTest1()
In Static Block of Test1
inside main method of Test 1
in static method of TestLab Coming from Line 26
b is 7
于 2017-03-16T13:16:34.070 回答
3

使用逐步调试器检查对象构造过程将非常有帮助,您可以在其中查看对象如何通过各个阶段。我发现这对于从更高的角度清除视角非常有用。Eclipse 可以通过它的调试器步骤来帮助您解决这个问题。

于 2015-12-10T10:14:56.303 回答
2

这是我在准备认证时发现的。

当我们运行一个类时,首先会发生静态块/静态变量初始化。如果有多个静态块,它会按照出现的顺序执行,

然后它会执行初始化块/实例变量初始化。如果有多个初始化块/变量初始化,它将按照出现的顺序执行,

之后它将查看构造函数。

于 2017-09-14T17:50:20.260 回答
1

将类加载到 JVM 时执行静态块。而 init 块被复制到构造函数中,该构造函数将在创建对象之前创建并运行其对象。

于 2015-07-16T19:24:13.630 回答
1

控制流是-

静态块 -> 初始化块 -> 最后是构造函数。

静态块->当控件进入类时,这个静态块只会被执行一次。(JVM加载这个类)

初始化块->每当为类创建新对象时,都会执行此初始化块(它将从构造函数的第二个语句执行,然后是构造函数语句-记住构造函数的第一个语句将是 Super()/this() )

Constructor -> 每当创建新对象时都会获取此信息。

于 2016-10-03T11:21:28.440 回答
0
  1. 静态初始化块仅在类加载时执行。
  2. Init 块每次在创建类的对象之前执行。

通过此视频了解更多信息:Rajan Jain 的 Java 教程:Java Video14 中的静态初始化块和初始化块 - YouTube

于 2018-11-04T19:48:08.893 回答