Java中的执行顺序

5
我正在尝试理解《Java编程思想》中的这个例子
package c07;
import com.bruceeckel.simpletest.*;

class Meal {
    Meal() { System.out.println("Meal()"); }
}

class Bread {
    Bread() { System.out.println("Bread()"); }
}

class Cheese {
    Cheese() { System.out.println("Cheese()"); }
}

class Lettuce {
    Lettuce() { System.out.println("Lettuce()"); }
}

class Lunch extends Meal {
    Lunch() { System.out.println("Lunch()"); }
}

class PortableLunch extends Lunch {
    PortableLunch() { System.out.println("PortableLunch()");}
}

public class Sandwich extends PortableLunch {
    private static Test monitor = new Test();
    private Bread b = new Bread();
    private Cheese c = new Cheese();
    private Lettuce l = new Lettuce();

    public Sandwich() {
        System.out.println("Sandwich()");
    }

    public static void main(String[] args) {
        new Sandwich();
        monitor.expect(new String[] {
          "Meal()",
          "Lunch()",
          "PortableLunch()",
          "Bread()",
          "Cheese()",
          "Lettuce()",
          "Sandwich()"
        });
    }
}

根据Java语言规范,执行顺序始于加载包含主方法的类。然后必须初始化此类的所有静态变量和成员变量(在此之前必须初始化超类的所有成员变量,尽管在这种情况下没有这些变量)。
因此,我认为在main开始执行之前,b、c和l将被初始化。但从输出结果来看似乎并非如此。我是否漏掉了什么?
3个回答

5
不,bc实例变量。
类中包含main的自动实例化不存在。只有静态变量被初始化。这就好比某个外部调用者写了下面的代码:
Sandwich.main(args);

所以当你写下这句话时:

Then all the statics and member variables of this class must be initialized

...那是不对的。只有静态变量被初始化,就像普通变量一样。

感谢Jon的回答。这是我在JLS中读到的(它说所有类变量都被初始化,而不仅仅是静态变量):在我们持续的示例中,Java虚拟机仍在尝试执行Test类的main方法。只有在类已经被初始化(§12.4.1)的情况下才允许这样做。初始化包括按文本顺序执行类变量初始化程序和类Test的静态初始化程序。但是,在Test可以被初始化之前,必须初始化其直接超类以及其直接超类的直接超类等等,递归地进行。 - Jin
“类变量” == “静态变量”。请参阅http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.3,“类变量是在类声明(§8.3.1.1)中使用关键字static声明的字段,或者在接口声明(§9.3)中带或不带关键字static声明的字段。” - Jon Skeet
抱歉,我有点困惑。如果“类变量”等于“静态变量”,为什么会说“类变量初始化器和静态初始化器”呢? - Jin
甚至在这本书的更早部分,作者就说“变量在任何方法被调用之前都会被初始化——即使是构造函数”,并且此处的示例中没有静态变量:http://www.linuxtopia.org/online_books/programming_books/thinking_in_java/TIJ306_014.htm - Jin
@Jin:这是在调用构造函数的情况下。 - Jon Skeet
@Jin:JLS将静态变量称为“类变量”。大多数人只是称它们为静态变量,但要注意的是,“类变量”这个术语不包括实例变量。请注意,静态初始化程序与类变量初始化程序是不同的。它们是像这样的块:static { /* code here */ } - Jon Skeet

0

示例输出是正确的。以下是重要规则:

  • 创建类时,必须首先调用超类的构造函数。这会冒泡到Object类。

  • 在调用构造函数之前,将调用成员变量初始化。

此示例中没有涉及任何static,除了技术上的monitor


0

JLS # 12.4.1. 当初始化发生时

类或接口类型T将在以下任何一个事件的第一次出现之前立即初始化:

  • T是一个类并且T的实例被创建。
  • T是一个类并且由T声明的静态方法被调用。
  • T声明的静态字段被分配。
  • T声明的静态字段被使用且该字段不是常量变量(§4.12.4)。
  • T是一个顶级类(§7.6),并且在T(§8.1.3)中词法嵌套的assert语句(§14.10)被执行。

JLS # 12.5. 创建新类实例

每当创建一个新的类实例时,都会为其分配内存空间,其中包含在类类型中声明的所有实例变量和在类类型的每个超类中声明的所有实例变量的空间,包括可能被隐藏的所有实例变量(§8.3)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接