Skip to content

Latest commit

 

History

History
674 lines (356 loc) · 155 KB

16-明确赋值.md

File metadata and controls

674 lines (356 loc) · 155 KB

第十六章 明确赋值

每个局部变量(14.4)和每个空 final 字段(4.12.4,8.3.1.2)必须被明确地赋值,当任何访问他们的值发生时。

其值的访问由表达式中除作为简单赋值操作符 =(15.26.1)的左侧操作数之外的任何位置出现的变量名称(或者,对于字段,由 this 限定的字段的简单名称)组成。

对于局部变量或空 final 字段 x 的每个访问,在访问前 x 必须是被明确赋值的,否则发生编译时错误。

类似地,每个空 final 变量必须最多赋值一次;当其赋值发生时,它必须是明确未赋值的。

这种赋值被定义为,当且仅当变量(或者,对于字段,其由 this 限定的简单名称)的简单名称出现在赋值操作符的左手边。

对于空 fina 变量的每个赋值,在赋值前变量必须是明确未赋值的,否则发生编译时错误。

本章的其余部分专门讨论“在 ... 之前明确赋值”和“在 ... 之前明确未赋值”用词的精确解释。

明确赋值背后的观点是,局部变量或空 final 字段的赋值必须发生在访问的每个可能执行路径上。类似地,明确未赋值背后的观点是,不允许空 final 变量的其他赋值发生在赋值的任何可能执行路径上。

分析考虑了语句和表达式的结构;它还提供了表达式操作符 !、&&、|| 和 ? : 以及布尔值常量表达式的特殊处理。

除条件布尔操作符 &&、|| 和 ? : 以及布尔值常量表达式的特殊处理之外,在流分析中不考虑表达式的值。

avatar avatar

avatar avatar

avatar avatar

为了精确描述明确赋值的所有情况,本节中的规则定义了几个技术术语:

    * 变量在语句或表达式之前是否是明确赋值的;

    * 变量在语句或表达式之前是否是明确未赋值的;

    * 变量在语句或表达式之后是否是明确赋值的;和

    * 变量在语句或表达式之后是否是明确未赋值的。

对于布尔值表达式,最后两种被重新定义为四种情况:

    * 当 true 时,变量在表达式之后是否是明确赋值的;

    * 当 true 时,变量在表达式之后是否是明确未赋值的;

    * 当 false 时,变量在表达式之后是否是明确赋值的;和

    * 当 false 时,变量在表达式之后是否是明确未赋值的。

在这,当 ture 和 当 false 表示表达式的值。

avatar

说法“V 在 X 之后是明确赋值的”(其中 V 是一个局部变量,X 是一个语句或表达式)意味着“当 X 正常完成时,V 在 X 之后是明确赋值的”。如果 X 突然完成,则赋值不必发生,此处所述的规则考虑到了这一点。

此定义的一个古怪结果是,“V 在 break; 之后是明确赋值的”总是 ture!因为 break 语句从不正常完成,如果 break 语句正常完成,则 V 已经被赋值,这毫无意义地为 true。

语句“V 在 X 之后是明确未赋值的”(其中 V 是一个变量,X 是一个语句或表达式)意味着“当 X 正常完成时,V 在 X 之后是明确未赋值的”。

此定义的一个更古怪的结果是,“V 在 break; 之后是明确未赋值的”总是 true!因为 break 语句从不正常完成,如果 break 语句正常完成,则 V 还未赋值,这毫无意义地为 true。(对于这个问题,如果 break 语句正常完成,要相信最荒唐的事,这也毫无意义地为 ture。)

在所有情况下,在语句或表达式执行后变量 V 有四种可能:

    * V 是明确赋值的,不是明确未赋值的。

      (流分析规则证实,V 的赋值已经发生。)

    * V 是明确未赋值的,不是明确赋值的。

      (流分析规则证实,V 的赋值还未发生。)

    * V 不是明确赋值的,不是明确未赋值的。

      (规则无法证实,V 的赋值是否已发生。)

    * V 是明确赋值的,是明确未赋值的。

      (语句或表达式可能正常完成。)

为了缩短规则,使用习惯的缩写“iff”来表示“当且仅当”。我们还使用一个缩写约定:当一个规则包含一个或多个“[未]赋值”出现时,则它代表两个规则,一个具有用“明确赋值”替换每个“[未]赋值”的出现,一个具有用“明确未赋值”替换“[未]赋值”的出现。

avatar

除非通过其他方式显式声明,否则通过本章的其他部分,我们将把 V 写作一个在作用域(6.3)中的局部变量或空 final 字段。同样,我们将使用 a、b 和 c 来表示表达式,S 和 T 来表示语句。我们将使用说法“a 是 V”来表示 a 是变量 V 的简单名称或 V 的由 this 限定的简单名称(忽略括号)。我们将使用说法“a 不是 V”来表示“a 是 V”的否定。

循环语句的明确未赋值分析引发了一个特殊的问题。思考语句 while (e) S。为了确定 V 在 e 的某个子表达式内是否是明确未赋值,我们需要在 e 之前确定 V 是否是明确未赋值。你可能主张,通过与明确赋值(16.2.10)规则类比,V 在 e 之前是明确未赋值,当且仅当它在 while 语句之前是明确未赋值。但是,这种规则对我们的目的来说是不够的。如果 e 计算为 true,则将执行语句 S。随后,如果 V 是由 S 赋值的,则在后面的迭代中当计算 e 时,V 已经赋值。根据上文建议的规则,可能会对 V 进行多次赋值,这正是我们寻求的通过引入这些规则以避免的。

修订后的规则是:“V 在 e 之前是明确未赋值,当且仅当它在 while 语句之前是明确未赋值且在 S 之后是明确未赋值”。但是,当我们为 S 制定规则时,发现:“V 在 S 之前是明确未赋值,当且仅当当 true 时,它在 e 之后是明确未赋值”。这导致一个循环。实际上,V 在循环条件 e 之前是明确未赋值,仅当它在循环作为一个整体之后是明确未赋值。

我们使用循环条件和 body 的假想分析来打破这个恶性循环。例如,如果我们假设,V 在 e 之前是明确未赋值(不管 V 在 e 之前是否真的是明确未赋值),然后可以证实,V 在 e 之后是明确未赋值,那么我们知道 e 未对 V 进行赋值。这更正式地陈述为:

假设 V 在 e 之前是明确未赋值,则 V 在 e 之后是明确未赋值。

上述分析的变种用于为 Java 编程语言中的所有循环语句定义具有良好基础的明确未赋值规则。

16.1. 明确赋值和表达式

16.1.1. 布尔常量表达式

    * 当 false 时,V 在任何其值为 true 的常量表达式(15.28)之后是[未]赋值。

    * 当 true 时,V 在其值为 false 的任何常量表达式之后是[未]赋值。

    * 当 true 时,V 在其值为 true 的任何常量表达式之后是[未]赋值,当且仅当 V 在常量表达式之前是[未]赋值。

    * 当 false 时,V 在其值为 false 的任何常量表达式之后是[未]赋值,当且仅当 V 在常量表达式之前是[未]赋值。

    * V 在布尔值常量表达式 e 之后是[未]赋值,当且仅当当 true 时 V 在 e 之后是[未]赋值且当 false 时 V 在 e 之后是[未]赋值。

      这等于说,V 在 e 之后是[未]赋值,当且仅当 V 在 e 之前是[未]赋值。

由于值为 true 的常量表达式从不会有值 false,值为 false 的常量表达式从不会有值 true,因此毫无意义地满足前两个规则。他们在分析涉及操作符 &&(16.1.2)、||(16.1.3)、!(16.1.4)和 ? :(16.1.5)的表达式时非常有用。

16.1.2. 条件与操作符 &&

    * 当 true 时,V 在 a && b(15.23)之后是[未]赋值,当且仅当当 true 时,V 在 b 之后是[未]赋值。

    * 当 false 时,V 在 a && b 之后是[未]赋值,当且仅当当 false 时,V 在 a 之后是[未]赋值,且当 false 时,V在 b 之后是[未]赋值。

    * V 在 a 之前是[未]赋值,当且仅当 V 在 a && b 之前是[未]赋值。

    * V 在 b 之前是[未]赋值,当且仅当当 true 时,V 在 a 之后是[未]赋值。

    * V 在 a && b 之后是[未]赋值,当且仅当当 true 时,V 在 a && b 之后是[未]赋值,且当 false 时,V 在 a && b 之后是[未]赋值。

16.1.3. 条件或操作符 ||

    * 当 true 时,V 在 a || b(15.24)之后是[未]赋值,当且仅当当 true 时,V 在 a 之后是[未]赋值,且当 true 时,V 在 b 之后是[未]赋值。

    * 当 false 时,V 在 a || b 之后是[未]赋值,当且仅当当 false 时,V 在 b 之后是[未]赋值。

    * V 在 a 之前是[未]赋值,当且仅当 V 在 a || b 之前是[未]赋值。

    * V 在 b 之前是[未]赋值,当且仅当当 false 时,V 在 a 之后是[未]赋值。

    * V 在 a || b 之后是[未]赋值,当且仅当当 true 时,V 在 a || b 之后是[未]赋值,且当 false 时,V 在 a || b 之后是[未]赋值。

16.1.4. 逻辑补码操作符 !

    * 当 true 时,V 在 !a(15.15.6)之后是[未]赋值,当且仅当当 false 时,V 在 a 之后是[未]赋值。

    * 当 false 时,V 在 !a 之后是[未]赋值,当且仅当当 true 时,V 在 a 之后是[未]赋值。

    * V 在 a 之前是[未]赋值,当且仅当 V 在 !a 之前是[未]赋值。

    * V 在 !a 之后是[未]赋值,当且仅当当 true 时,V 在 !a 之后是[未]赋值,且当 false 时,V 在 !a 之后是[未]赋值 。

      这等于说,V 在 !a 之后是[未]赋值,当且仅当 V 在 a 之后是[未]赋值。

16.1.5. 条件操作符 ? :

假设,b 和 c 是布尔值表达式。

    * 当 true 时,V 在 a ? b : c(15.25)之后是[未]赋值,当且仅当当 true 时,V 在 b 之后是[未]赋值,且当 true 时,V 在 c 之后是[未]赋值。

    * 当 false 时,V 在 a ? b : c 之后是[未]赋值,当且仅当当 false 时,V 在 b 之后是[未]赋值,且当 false 时,V 在 c 之后是[未]赋值。

    * V 在 a 之前是[未]赋值,当且仅当 V 在 a ? b : c 之前是[未]赋值。

    * V 在 b 之前是[未]赋值,当且仅当当 true 时,V 在 a 之后是[未]赋值。

    * V 在 c 之前是[未]赋值,当且仅当当 true 时,V 在 a 之后是[未]赋值。

    * V 在 a ? b : c 之后是[未]赋值,当且仅当当 true 时,V 在 a ? b : c 之后是[未]赋值,且当 false 时,V 在 a ? b : c 之后是[未]赋值。

16.1.6. 条件操作符 ? :

假设,b 和 c 是非布尔值的表达式。

    * V 在 a ? b : c(15.25)之后是[未]赋值,当且仅当 V 在 b 之后是[未]赋值,且 V 在 c 之后是[未]赋值。

    * V 在 a 之后是[未]赋值,当且仅当 V 在 a ? b : c 之前是[未]赋值。

    * V 在 b 之前是[未]赋值,当且仅当当 true 时,V 在 a 之后是[未]赋值。

    * V 在 c 之前是[未]赋值,当且仅当当 false 时,V 在 a 之后是[未]赋值。

16.1.7. 其他布尔类型的表达式

假设,e 是布尔类型的表达式,且不是布尔常量表达式、逻辑补码表达式 !a、条件与表达式 a && b、条件或表达式 a || b 或条件表达式 a ? b : c。

    * 当 true 时,V 在 e 之后是[未]赋值,当且仅当 V 在 e 之后是[未]赋值。

    * 当 false 时,V 在 e 之后是[未]赋值,当且仅当 V 在 e 之后是[未]赋值。

16.1.8. 赋值表达式

考虑一个赋值表达式 a=b、a+=b、a-=b、a*=b、a/=b、a%=b、a<<=b、a>>=b、a>>>=b、a&=b、a|=b 或 a^=b(15.26)。

    * V 在赋值表达式之后是明确赋值,当且仅当以下任一:

        a 是 V,或

        V 在 b 之后是明确赋值。

    * V 在赋值表达式之后是明确未赋值,当且仅当 a 不是 V 且 V 在 b 之后是明确未赋值。

    * V 在 a 之前是[未]赋值,当且仅当 V 在赋值表达式之前是[未]赋值。

    * V 在 b 之前是[未]赋值,当且仅当 V 在 a 之后是[未]赋值。

请注意,当 a 是 V,且 V 在如 a &= b 之类的复合赋值之前不是明确赋值,则必然会发生编译时错误。上面所述的明确赋值的第一条规则还包括不相连的“a 是 V”甚至复合赋值表达式,不仅仅是简单赋值,因此 V 在后面的代码处将被视为已明确赋值。包括不相连的“a 是 V”并不会影响有关程序是否是可接受的或是否会产生编译时错误的二进制确定,但它会影响代码中多少不同点可能被认为是错误的,因此在实践中它可以改进错误报告的质量。类似的注意也适用于上述明确未赋值的第一条规则中的不相连的“a 不是 V”。

16.1.9. 操作符 ++ 和 --

    * V 在 ++a(15.15.1)、--a(15.15.2)、a++(15.14.2)或 a--(15.14.3)之后是明确赋值,当且仅当 a 是 V 或 V 在操作数表达式之后是明确赋值。

    * V 在 ++a、--a、a++ 或 a-- 之后是明确未赋值,当且仅当 a 不是 V 且 V 在操作数表达式之后是明确未赋值。

    * V 在 a 之前是[未]赋值,当且仅当 V 在 ++a、--a、a++ 或 a-- 之前是[未]赋值。

16.1.10. 其他表达式

如果表达式不是布尔常量表达式,不是前置自增表达式 ++a、前置自减表达式 --a、后置自增表达式 a++、后置自减表达式 a--、逻辑补码表达式 !a、条件与表达式 a && b、条件或表达式 a || b、条件表达式 a ? b : c 或赋值表达式,则适用以下规则:

    * 如果表达式没有子表达式,则 V 在表达式之后是[未]赋值,当且仅当 V 在表达式之前是[未]赋值。

      此情况适用于字面量、名称、this(限定和非限定)、非限定的无参数类实例创建表达式、初始化的初始化器不包含表达式的数组创建表达式、非限定的父类字段访问表达式、命名的无参数方法调用和非限定的无参数父类方法调用。

    * 如果表达式有子表达式,则 V 在表达式之后是[未]赋值,当且仅当 V 在它的最右侧直接子表达式之后是[未]赋值。

在断言,可以知道变量 V 在方法调用之后是明确未赋值,的背后有一个微妙的推理。就本身来说,在表面值和没有严格限制条件下,这种断言并不总是 true,因为被调用的方法可以执行赋值。但必须记住,为了保证 Java 编程语言的实现,明确未赋值概念仅适用于空 final 变量。如果 V 是一个空 final 局部变量,则只有其声明所属的方法可以执行对 V 的赋值。如果 V 是一个空 final 字段,则只有构造器或包含 V 的声明的类的初始化器可以执行对 V 的赋值;方法不可以执行对 V 的赋值。最后,对显式构造器调用(8.8.7.1)进行了特殊处理(16.9);尽管他们在句法上类似包含方法调用的表达式语句,但他们不是表达式语句,因此本节的规则并不适用于显式构造器调用。

对于表达式 x 的任何直接子表达式 y,V 在 y 之前是[未]赋值,当且仅当以下之一为 true:

    * y 是 x 最左侧的直接子表达式,V 在 x 之前是[未]赋值。

    * y 是二元操作的右侧操作数,V 在左侧操作数之后是[未]赋值。

    * x 是数组访问,y 是括号中的子表达式,V 在子表达式之后括号之前是[未]赋值。

    * x 是主方法调用表达式,y 是方法调用表达式中的第一个参数表达式,V 在计算目标对象的主表达式之后是[未]赋值。

    * x 是方法调用表达式或类实例创建表达式;y 是参数表达式,但不是第一个,V 在 y 左侧的参数表达式之后是[未]赋值。

    * x 是限定的类实例创建表达式,y 是类实例创建表达式中的第一个参数表达式,V 在计算限定对象的主表达式之后是[未]赋值。

    * x 是数组实例创建表达式;y 是维度表达式,但不是第一个,V 在 y 左侧的维度表达式之后是[未]赋值。

    * x 是通过数组初始化器初始化的数组实例创建表达式,y 是 x 中的数组初始化器;V 在 y 左侧的维度表达式之后是[未]赋值。

16.2. 明确赋值和语句

16.2.1. 空语句

    * V 在空语句(14.6)之后是[未]赋值,当且仅当它在空语句之前是[未]赋值。

16.2.2. 块

    * 空 final 成员字段 V 在其为 V 的作用域中的任何方法的 body 的块(14.2)和 V 的作用域中声明的任何类声明之前是明确赋值(同时不是明确未赋值)。

    * 局部变量 V 在其为声明 V 的构造器、方法、实例初始化器或 static 初始化器的 body 的块之前是明确未赋值(同时不是明确赋值)。

    * 让 C 是 V 的作用域中声明的类。然后 V 在其为 C 中声明的任何构造器、方法、实例初始化器或 static 初始化器的 body 的块之前是明确赋值,当且仅当 V 在 C 的声明之前是明确赋值。

      请注意,没有这样的规则,允许我们得出结论,V 在其为 C 中声明的任何构造器、方法、实例初始化器或 static 初始化器的 body 的块之前是明确未赋值。我们可以非正式地得出结论,V 在 C 中声明的任何构造器、方法、实例初始化器或 static 初始化器的 body 的块之前不是明确未赋值,但是不需要显式地声明这种规则。

    * V 在空块之后是[未]赋值,当且仅当 V 在空块之前是[未]赋值。

    * V 在非空块之后是[未]赋值,当且仅当 V 在块中最后一个语句之后是[未]赋值。

    * V 在块的第一个语句之前是[未]赋值,当且仅当 V 在块之前是[未]赋值。

    * V 在块的任何其他语句 S 之前是[未]赋值,当且仅当 V 在块中直接在 S 前面的语句之后是[未]赋值。

我们称 V 在块 B 的任何位置是明确未赋值,当且仅当:

    * V 在 B 之前是明确未赋值。

    * V 在出现在 B 中的每个赋值表达式 V = e、V += e、V -= e、V *= e、V /= e、V %= e、V <<= e、V >>= e、V >>>= e、V &= e、V |= e 或 V ^= e 中的 e 之后是明确赋值。

    * V 在出现在 B 中的每个表达式 ++V、--V、V++ 或 V-- 之前是明确赋值。

这些条件是违反直觉的,需要一些解释。考虑一个简单的赋值 V = e。如果 V 在 e 之后是明确赋值,则:

    * 赋值出现在死代码中,V 毫无意义是明确赋值。在这种情况下,赋值实际上不会发生,我们可以假定,V 没有被赋值表达式赋值。或:

    * V 已经由 e 之前更早的表达式赋值。在这种情况下,当前赋值将导致编译时错误。

因此,我们可以得出这样的结论,如果由不会导致编译时错误的程序满足这些条件,则 B 中任何对 V 的赋值实际上在运行时不会发生。

16.2.3. 局部类声明语句

    * V 在局部类声明语句(14.3)之后是[未]赋值,当且仅当 V 在局部类声明语句之前是[未]赋值。

16.2.4. 局部变量声明语句

    * V 在不包含变量初始化器的局部变量声明语句(14.4)之后是[未]赋值,当且仅当 V 在局部变量声明语句之前是[未]赋值。

    * V 在包含至少一个变量初始化器的局部变量声明语句之后是明确赋值,当且仅当 V 在局部变量声明语句的最后一个变量初始化器或其在声明 V 的声明符中的声明中的最后一个变量初始化器之后是明确赋值。

    * V 在包含至少一个变量初始化器的局部变量声明语句之后是明确未赋值,当且仅当 V 在局部变量声明语句的最后一个变量初始化器和其不在声明 V 的声明符中的声明中的最后变量初始化器之后是明确未赋值。

    * V 在局部变量声明语句中的第一个变量初始化器之前是[未]赋值,当且仅当 V 在局部变量声明语句之前是[未]赋值。

    * V 在局部变量声明语句中除第一个之外的任何变量初始化器 e 之前是明确赋值,当且仅当 V 在 e 左侧的变量初始化器或其在声明 V 的声明符中的 e 左侧的初始化器表达式之后是明确赋值。

    * V 在局部变量声明语句中的除第一个之外的任何变量初始化器 e 之前是明确未赋值,当且仅当 V 在 e 左侧的变量初始化器和其不在声明 V 的声明符中的 e 左侧的初始化器表达式之后是明确未赋值。

16.2.5. 带标记的语句

    * V 在带标记的语句 L : S(其中 L 是一个标记)(14.7)之后是[未赋值],当且仅当 V 在 S 之后是[未]赋值且 V 在可以离开带标记的语句 L : S 的每个 break 语句之前是[未]赋值。

    * V 在 S 之前是[未]赋值,当且仅当 V 在 L : S 之前是[未]赋值。

16.2.6. 表达式语句

    * V 在表达式语句 e;(14.8)之后是[未]赋值,当且仅当它在 e 之后是[未]赋值。

    * V 在 e 之前是[未]赋值,当且仅当它在 e; 之前是[未]赋值。

16.2.7. if 语句

以下规则适用于语句 if (e) S(14.9.1):

    * V 在 if (e) S 之后是[未]赋值,当且仅当 V 在 S 之后是[未]赋值且当 false 时 V 在 e 之后是[未]赋值。

    * V 在 e 之前是[未]赋值,当且仅当 V 在 if (e) S 之前是[未]赋值。

    * V 在 S 之前是[未]赋值,当且仅当当 true 时 V 在 e 之后是[未]赋值。

以下规则适用于语句 if (e) S else T(14.9.2):

    * V 在 if (e) S else T 之后是[未]赋值,当且仅当 V 在 S 之后是[未]赋值且 V 在 T 之后是[未]赋值。

    * V 在 e 之前是[未]赋值,当且仅当 V 在 if (e) S else T 之前是[未]赋值。

    * V 在 S 之前是[未]赋值,当且仅当当 true 时 V 在 e 之后是[未]赋值。

    * V 在 T 之前是[未]赋值,当且仅当当 false 时 V 在 e 之后[未]赋值。

16.2.8. assert 语句

以下规则同时适用于语句 assert e1 和语句 assert e1 : e2(14.10):

    * V 在 e1 之前是[未]赋值,当且仅当 V 在 assert 语句之前是[未]赋值。

    * V 在 assert 语句之后是明确赋值,当且仅当 V 在 assert 语句之前是明确赋值。

    * V 在 assert 语句之后是明确未赋值,当且仅当 V 在 assert 语句之前是明确未赋值且当 true 时 V 在 e1 之后是明确未赋值。

以下规则适用于语句 assert e1 : e2:

V 在 e2 之前是[未]赋值,当且仅当当 false 时 V 在 e1 之后是[未]赋值。

16.2.9. switch 语句

    * V 在 switch 语句(14.11)之后是[未]赋值,当且仅当以下所有都为 true:

        在 switch 块中有一个 default 标记或 V 在 switch 表达式之后是[未]赋值。

        在 switch 块中没有开始块语句组的 switch 标记(即,在结束 switch 块的“}”之前没有 switch 标记)或 V 在 switch 表达式之后是[未]赋值。

        switch 块不包含块语句组或 V 在最后一块语句组的最后一个块语句之后是[未]赋值。

        V 在每个可能离开 switch 语句的 break 语句之前是[未]赋值。

    * V 在 switch 表达式之前是[未]赋值,当且仅当 V 在 switch 语句之前是[未]赋值。

如果 switch 块包含至少一个块语句组,则以下规则也适用:

    * V 在 switch 块的第一个块语句组的第一个块语句之前是[未]赋值,当且仅当 V 在 switch 表达式之后是[未]赋值。

    * V 在除第一个之外的任何块语句组的第一个块语句之前是[未]赋值,当且仅当 V 在 switch 表达式之后是[未]赋值且 V 在前一个块语句之后是[未]赋值。

16.2.10. while 语句

    * V 在 while (e) S(14.12)之后是[未]赋值,当且仅当 V 在 e 之后是[未]赋值且 V 在以此 while 语句作为 break 目标的每个 break 语句之前是[未]赋值。

    * V 在 e 之前是明确赋值,当且仅当 V 在 while 语句之前是明确赋值。

    * V 在 e 之前是明确未赋值,当且仅当以下所有为 true:

        V 在 while 语句之前是明确未赋值。

        假定 V 在 e 之前是明确未赋值,则 V 在 S 之后是明确未赋值。

        假定 V 在 e 之前是明确未赋值,则 V 在以此 while 语句作为 continue 目标的每个 continue 语句之前是明确未赋值。

    * V 在 S 之前是[未]赋值,当且仅当当 true 时 V 在 e 之后是[未]赋值。

16.2.11. do 语句

    * V 在 do S while (e) ;(14.13)之后是[未]赋值,当且仅当当 false 时 V 在 e 之后是[未]赋值且 V 在以此 do 语句作为 break 目标的每个 break 语句之前是[未]赋值。

    * V 在 S 之前是明确赋值,当且仅当 V 在 do 语句之前是明确赋值。

    * V 在 S 之前是明确未赋值,当且仅当以下所有为 true:

        V 在 do 语句之前是明确未赋值。

        假定 V 在 S 之前是明确未赋值,则当 true 时 V 在 e 之后是明确未赋值。

    * V 在 e 之前是[未]赋值,当且仅当 V 在 S 之后是[未]赋值且 V 在以此 do 语句作为 continue 目标的每个 continue 语句之前是[未]赋值。

16.2.12. for 语句

此处的规则涵盖了基本 for 语句(14.14.1)。由于增强 for 语句(14.14.2)被定为为转换为基本 for 语句,因此不需要为它提供特殊的规则。

    * V 在 for 语句之后是[未]赋值,当且仅当以下两个都为 true:

        不存在条件表达式或当 false 时 V 在条件表达式之后是[未]赋值。

        V 在以此 for 语句为 break 目标的每个 break 语句之前是[未]赋值。

    * V 在 for 语句初始化部分之前是[未]赋值,当且仅当 V 在 for 语句之前是[未]赋值。

    * V 在 for 语句的条件部分之前是明确赋值,当且仅当 V 在 for 语句的初始化部分之后是明确赋值。

    * V 在 for 语句的条件部分之前是明确未赋值,当且仅当以下所有都为 true:

        V 在 for 语句的初始化部分之后是明确未赋值。

        假定 V 在 for 语句的条件部分之前是明确未赋值,则 V 在包含的语句之后是明确未赋值。

        假定 V 在包含的语句之前是明确未赋值,则 V 在以此 for 语句为 continue 目标的每个 continue 语句之前是明确未赋值。

    * V 在包含的语句之前是[未]赋值,当且仅当以下任一为 true:

        存在条件表达式,当 true 时 V 在条件表达式之后是[未]赋值。

        不存在条件表达式,V 在 for 语句的初始化部分之后是[未]赋值。

    * V 在 for 语句的增量部分之前是[未]赋值,当且仅当 V 在包含的语句之后是[未]赋值且 V 在以此 for 语句为 continue 目标的每个 continue 语句之前是[未]赋值。

16.2.12.1. for 语句的初始化部分

    * 如果 for 语句的初始化部分是局部变量声明语句,则适用 16.2.4 的规则。

    * 否则,如果初始化部分是空的,则 V 在初始化部分之后是[未]赋值,当且仅当 V 在初始化部分之前是[未]赋值。

    * 否则,三个规则适用:

        V 在初始化部分之后是[未]赋值,当且仅当 V 在初始化部分的最后一个表达式语句之后是[未]赋值。

        V 在初始化部分的第一个表达式语句之前是[未]赋值,当且仅当 V 在初始化部分之前是[未]赋值。

        V 在除初始化部分的第一个之外的表达式语句 S 之前是[未]赋值,当且仅当 V 在直接在 S 前面的表达式语句之后是[未]赋值。

16.2.12.2. 语句的增量部分

    * 如果 for 语句的增量部分是空的,则 V 在增量部分之后是[未]赋值,当且仅当 V 在增量部分之前是[未]赋值。

    * 否则,三个规则适用:

        V 在增量部分之后是[未]赋值,当且仅当 V 在增量部分的最后一个表达式语句之后是[未]赋值。

        V 在增量部分的第一个表达式语句之前是[未]赋值,当且仅当 V 在增量部分之前是[未]赋值。

        V 在除增量部分的第一个之外的表达式语句 S 之前是[未]赋值,当且仅当 V 在直接在 S 前面的表达式语句之后是[未]赋值。

16.2.13. break、continue、return 和 throw 语句

    * 按照惯例,我们称,V 在任何 break、continue、return 或 throw 语句(14.15,14.16,14.17,14.18)之后是[未]赋值。

      变量在语句或表达式之后是“[未]赋值”这一概念实际上表示“在语句或表达式正常完成之后是[未]赋值”。因为 break、continue、return 或 throw 语句从不正常完成,所以它无意义地满足此概念。

    * 在具有表达式 e 的 return 语句或具有表达式 e 的 throw 语句中,V 在 e 之前是[未]赋值,当且仅当 V 在 return 或 throw 语句之前是[未]赋值。

16.2.14. synchronized 语句

    * V 在 synchronized (e) S(14.19)之后是[未]赋值,当且仅当 V 在 S 之后是[未]赋值。

    * V 在 e 之前是[未]赋值,当且仅当 V 在语句 synchronized (e) S 之前是[未]赋值。

    * V 在 S 之前是[未]赋值,当且仅当 V 在 e 之后是[未]赋值。

16.2.15. try 语句

这些规则适用于每个 try 语句(14.20),不管它是否有 finally 块:

    * V 在 try 块之前是[未]赋值,当且仅当 V 在 try 语句之前是[未]赋值。

    * V 在 catch 块之前是明确赋值,当且仅当 V 在 try 块之前是明确赋值。

    * V 在 catch 块之前是明确未赋值,当且仅当以下所有都为 true:

        V 在 try 块之后是明确未赋值。

        V 在属于 try 块的每个 return 语句之前是明确未赋值。

        V 在属于 try 块的每个 throw e 形式的语句的 e 之后是明确未赋值。

        V 在出现在 try 块的每个 assert 语句之后是明确未赋值。

        V 在属于 try 块的其 break 目标包含(或是)该 try 语句的每个 break 语句之前是明确未赋值。

        V 在属于 try 块的其 continue 目标包含此 try 语句的每个 continue 语句之前是明确未赋值。

如果 try 语句没有 finally 块,则此规则也适用:

    * V 在 try 语句之后是[未]赋值,当且仅当 V 在 try 块之后是[未]赋值且 V 在 try 语句的每个 catch 块之后是[未]赋值。

如果 try 语句没有 finally 块,则这些规则也适用:

    * V 在 try 语句之后是明确赋值,当且仅当以下至少一个为 true:

        V 在 try 块之后是明确赋值且 V 在 try 语句的每个 catch 块之后是明确赋值。

        V 在 finally 块之后是明确赋值。

        V 在 try 语句之后是明确未赋值,当且仅当 V 在 finally 块之后是明确未赋值。

    * V 在 finally 块之前是明确赋值,当且仅当 V 在 try 语句之前是明确赋值。

    * V 在 finally 块之前是明确未赋值,当且仅当以下所有都为 true:

        V 在 try 块之后是明确未赋值。

        V 在属于 try 块的每个 return 语句之前是明确未赋值。

        V 在属于 try 块的每个 throw e 形式的语句的 e 之后是明确未赋值。

        V 在出现在 try 块中的每个 assert 语句之后是明确未赋值。

        V 在属于 try 块的其 break 目标包含(或是)此 try 语句的每个 break 语句之前是明确未赋值。

        V 在属于 try 块的其 continue 目标包含此 try 语句的每个 continue 语句之前是明确未赋值。

        V 在 try 语句的每个 catch 块之后是明确未赋值。

16.3. 明确赋值和参数

    * 方法或构造器(8.4.1,8.8.1)的形式参数 V 在方法或构造器的 body 之前是明确赋值(同时不是明确未赋值)。

    * catch 子句(14.20)的异常参数 V 在 catch 子句的 body 之前是明确赋值(同时不是明确未赋值)。

16.4. 明确赋值和数组初始化器

    * V 在空数组初始化器(10.6)之后是[未]赋值,当且仅当 V 在空数组初始化器之前是[未]赋值。

    * V 在非空数组初始化器之后是[未]赋值,当且仅当 V 在数组初始化器的最后一个变量初始化器之后是[未]赋值。

    * V 在数组初始化器的第一个变量初始化器之前是[未]赋值,当且仅当 V 在数组初始化器之前是[未]赋值。

    * V 在数组初始化器的任何其他变量初始化器 e 之前是[未]赋值,当且仅当 V 在数组初始化器的 e 左侧的变量初始化器之后是[未]赋值。

16.5. 明确赋值和枚举常量

在 16.8 给定确定变量何时在枚举常量之前是明确赋值或明确未赋值的规则。

这是因为枚举常量实质上是一个用类实例创建表达式(15.9)初始化的 static final 字段(8.3.1.1,8.3.1.2)。

    * V 在在 V 的作用域中声明的无参枚举常量的类 body 声明之前是明确赋值,当且仅当 V 在枚举常量之前是明确赋值。

    * V 在在 V 的作用域中声明的有参枚举常量的类 body 声明之前是明确赋值,当且仅当 V 在枚举常量的最后一个参数表达式之后是明确赋值。

枚举常量的类 body 中的任何构造的明确赋值/未赋值状态由普通的类规则控制。

    * V 在枚举常量的第一个参数之前是[未]赋值,当且仅当它在枚举常量之前是[未]赋值。

    * V 在 y(枚举常量的一个参数,但不是第一个)之前是[未]赋值,当且仅当 V 在 y 左侧的参数之后是[未]赋值。

16.6. 明确赋值和匿名类

    * V 在在 V 的作用域中声明的匿名类声明(15.9.5)之前是明确赋值,当且仅当 V 在声明匿名类的类实例创建表达式之后是明确赋值。

应该清楚的是,如果匿名类由枚举常量隐式定义,则 16.5 的规则适用。

16.7. 明确赋值和成员类型

让 C 是类,让 V 是 C 的空 final 字段。则:

    * V 在 C 的任何成员类型(8.5,9.5)声明之前是明确赋值(同时不是明确未赋值)。

让 C 是在 V 的作用域中声明的类。则:

    * V 在 C 的成员类型声明之前是明确赋值,当且仅当 V 在 C 的声明之前是明确赋值。

16.8. 明确赋值和 static 初始化器

    * 让 C 是在 V 的作用域中声明的类。则:

V 在枚举常量(8.9.1)或 C 的 static 变量初始化器(8.3.2)之前是明确赋值,当且仅当 V 在 C 的声明之前是明确赋值。

      请注意,没有这样的规则,允许我们得出结论,V 在 static 变量初始化器或枚举常量之前是明确未赋值。我们可以非正式地得出结论,V 在 C 的任何 static 变量初始化器之前不是明确未赋值,但不需要显式地声明这种规则。

让 C 是类,让 V 是 C 的空 static final 成员字段,声明在 C 中。则:

    * V 在 C 的最左边的枚举常量、static 初始化器(8.7)或 static 变量初始化器之前是明确未赋值(同时不是明确赋值)。

    * V 在除最左边之外的 C 的枚举常量、static 初始化器或 static 变量初始化器之前是[未]赋值,当且仅当 V 在在 C 的枚举常量、static 初始化器或 static 变量初始化器前面的之后是[未]赋值。

让 C 是类,让 V 是 C 的声明在 C 的父类中的空 static final 成员字段。则:

    * V 在 C 的每个枚举常量之前是明确赋值(同时不是明确未赋值)。

    * V 在其是 C 的 static 初始化器的 body 的块之前是明确赋值(同时不是明确未赋值)。

    * V 在 C 的每个 static 变量初始化器之前是明确赋值(同时不是明确未赋值)。

16.9. 明确的赋值、构造器和实例初始化器

让 C 是声明在 V 的作用域中的类。则:

    * V 在 C 的实例变量初始化器(8.3.2)之前是明确赋值的,当且仅当 V 在 C 的声明之前是明确赋值的。

      请注意,没有这样的规则,允许我们得出结论,V 在实例变量初始化器之前是明确未赋值的。我们可以非正式地得出结论,V 在 C 的任何实例变量初始化器之前不是明确未赋值的,但不需要显式地声明这样的规则。

让 C 是类,让 V 是在 C 中声明的 C 的空 final 非 static 成员字段。则:

    * V 在最左边实例初始化器(8.6)或 C 的实例变量初始化器之前是明确未赋值(同时不是明确赋值的)的。

    * V 在除最左边之外的 C 的实例初始化器或实例变量初始化器之前是[未]赋值的,当且仅当 V 在在 C 的实例初始化器或实例变量初始化器后面的之后是[未]赋值的。

以下规则在类 C 的构造器(8.8.7)下仍然保持:

    * V 在代替的构造器调用(8.8.7.1)之后是明确赋值的(同时不是明确未赋值的)。

    * V 在显式或隐式的父类构造器调用(8.8.7.1)之前是明确未赋值的(同时不是明确赋值的)。

    * 如果 C 没有实例初始化器或实例变量初始化器,则 V 在显式或隐式的父类构造器调用之后不是明确赋值的(同时是明确未赋值的)。

    * 如果 C 有至少一个实例初始化器或实例变量初始化器,则 V 在显式或隐式的父类构造器调用之后是[未]赋值的,当且仅当 V 在 C 最右边的实例初始化器或实例变量初始化器之后是[未]赋值的。

让 C 是类,让 V 是在 C 的父类中声明的 C 的空 final 成员字段。则:

    * V 在其是构造器 body 或 C 的实例初始化器的块之前是明确赋值的(同时不是明确未赋值的)。

    * V 在 C 的每个实例变量初始化器之前是明确赋值的(同时不是明确未赋值的)。