什么“T是顶级类,并且在T中执行词法嵌套的断言语句。”是什么意思?

我正在学习“类和接口的初始化”,它说“T是一个顶级类,并且在T中执行词法嵌套的断言语句”。 任何人都可以告诉我“T是一个顶级类,并且执行一个词法嵌套在T中的断言语句”。 举个例子?

这句话来自JLS ,原文是这样的:

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

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

我可以给它一个部分解释。 它指的是启用/禁用断言。 断言由-ea vm参数启用。

assert一个重点是:

在其类完成初始化之前执行的assert语句已启用。

假设未给出-ea并且您运行以下代码:

  public class Q1 { public static void main(String[] args) { Bar b = new Bar(); } } class Bar { static { boolean enabled = false; assert enabled = false; //line(a) System.out.println("Asserts " + (enabled ? "enabled" : "disabled")); System.out.println("as"); Baz.testAsserts(); } } class Baz extends Bar { static void testAsserts() { boolean enabled = false; assert enabled = false; System.out.println("Asserts " + (enabled ? "enabled" : "disabled")); } } 

在上面的例子中,当b被初始化时,Java保证在调用line(a)之前,断言被禁用(即,行(a)根本不被执行)。 因为断言启用/禁用是类初始化的一部分,因此在您显示的声明中提到它。

原因,为什么提到顶级类而不是其他所有类都是这个 。 这里有更详细的行为:

 public class Q1 { public static void main(String[] args) { Baz.testAsserts(); // Will execute after Baz is initialized. } } class Bar { static { Baz.testAsserts(); // Will execute before Baz is initialized! } } class Baz extends Bar { static void testAsserts() { boolean enabled = false; assert enabled = false; System.out.println("Asserts " + (enabled ? "enabled" : "disabled")); } } 

甚至没有使用-ea标志,仍然会抛出AssertionException 。 这是发生的事情:

  1. Q1.main被调用
  2. Q1.main调用Baz.testAsserts。
  3. 因为Baz扩展了Bar和Bar没有初始化,所以根据JLS它会尝试初始化Bar
  4. 调用Bar的静态块。 记住assert语句在其类完成初始化或调用assert(首先发生)之前已启用。 在这种情况下,在这种情况下,由于Bar仍未完全初始化,因此
  5. Bar静态调用Baz.testAsserts() 。 断言仍然被启用(记得禁用断言与类初始化有关,而Bar仍未完全初始化)。 现在Baz.testAsserts()抛出AssertionException

上面是一个环孔。 JLS只保证在顶级类中执行任何assert之前,它将禁用/启用(如同给定的任何vm参数)。 但如果它不是顶级类,那么行为取决于顶级类的初始化。 要解释这一点,请看:

 class Bar { static { //Baz.testAsserts(); boolean enabled = false; assert enabled = false; System.out.println("Asserts " + (enabled ? "enabled" : "disabled")); // Will execute before Baz is initialized! } } class Baz extends Bar { static void testAsserts() { boolean enabled = false; assert enabled = false; System.out.println("Asserts " + (enabled ? "enabled" : "disabled")); } } 

这会打印Asserts disabled Asserts disabled因为Bar已初始化。 Bar初始化禁用类的assert ,因此禁用Baz

我知道如何阅读此规范,但OpenJDK 1.7.0_40的行为与指示不符,Oracle JDK 1.7.0_25也没有。

顶级类是不嵌套在任何其他类中的类。 断言语句可以在可执行代码中发生,即在方法,构造函数或静态初始化器块中。 大多数这些情况由其他项处理:静态方法已经被覆盖,其他方法以及构造函数都属于所述类的对象的创建,静态初始化程序块是初始化过程的一部分,这是任何结果其他事件。

因此,我能想到的唯一方法是在没有触发任何这些情况的情况下使用词法嵌套语句,这是通过嵌套类。 像这样的事情:

 class Outer { static { System.out.println("Outer initialized"); } static class Nested { static void foo() { assert System.out == null; } } } 

但是如果我使用启用的断言运行Outer.Nested.foo() ,那么我得到断言错误(所以语句被执行)但不是Outer initialized消息。 因此,即使执行了一个词法嵌套的断言语句,顶层​​类也没有初始化。

要么我在这里误解了规范,要么提到的实现不遵循它。

至于基本原理:我认为这个要求的要点是启用和禁用断言是通过类的隐藏静态(和iirc.final)字段实现的。 因此,当执行assert语句时,它必须检查该字段,因此必须初始化该字段,因此必须初始化该类。 但是在上面的代码中,相关字段可能是Outer.Nested ,而不是Outer本身。 因此,在这一点上不需要初始化Outer是有道理的。 但除了上述结构之外,我无法想到最后一条规则适用但不适用其他规则的情况。

您已链接到JLS for Java 7的第12.4.1节 。

在java bug JDK-8043189“12.4.1:应该断言外部类的触发器初始化?” 有一条评论说明列表的最后一个子弹,其中写着“T是顶级类,并且在词典中嵌套在T中的断言语句(§14.10)”是完全错误的,并且它被错误地插入到JLS早在版本6中,它一直呆在那里直到版本7只是因为没有人注意到它。

我发现JLS for Java 8中的语句仍然存在,但最终已从JLS for Java 9的第12.4.1段中删除。

所以,最重要的是,不要试图从中得到任何意义,因为没有任何东西可以制造; 这不是一个真实的陈述,它永远不会。

这是一个顶级课程:

 class TopLevel { ... } 

这是一个断言声明:

 assert( condition ); 

其中condition是一些布尔表达式。

如果A出现在B定义的花括号内,则它在词法上嵌套到B中。 例如,字段,方法,静态块在词法上定义嵌套在类定义中。 语句在词法上嵌套在方法或静态块中。 本地定义嵌套在方法或块中,它们本身嵌套在方法中。

因此,词汇表嵌套在顶级类中的断言语句可以是:

 class A { static { assert ( 2+2 == 4 ); } }