Value Classes and Objects

Changes to the Java® Language Specification • See also JVMS Changes & JVMS Changes for Strict Fields • Version 27-jep401ea3+1-1

This document describes changes to the Java Language Specification to support value classes and objects, a preview feature introduced by JEP 401.

Key changes include:

Changes are described with respect to existing sections of the JVM Specification. New text is indicated like this and deleted text is indicated like this. Explanation and discussion, as needed, is set aside in grey boxes.

Revision history:

Chapter 3: Lexical Structure

3.8 Identifiers

...

To facilitate the recognition of contextual keywords, the syntactic grammar (2.3) sometimes disallows certain identifiers by defining a production to accept only a subset of identifiers. The subsets are as follows:

TypeIdentifier:
Identifier but not permits, record, sealed,
value, var, or yield
UnqualifiedMethodIdentifier:
Identifier but not yield

TypeIdentifier is used in the declaration of classes, interfaces, and type parameters (8.1, 9.1, 4.4), and when referring to types (6.5). For example, the name of a class must be a TypeIdentifier, so it is illegal to declare a class named permits, record, sealed, value, var, or yield.

UnqualifiedMethodIdentifier is used when a method invocation expression refers to a method by its simple name (6.5.7.1). Since the term yield is excluded from UnqualifiedMethodIdentifier, any invocation of a method named yield must be qualified, thus distinguishing the invocation from a yield statement (14.21).

For the purpose of parsing, modifiers in class headers don't actually conflict with types, and so it would be possible to to recognize the value keyword (along with sealed) without disallowing it as a class name.

However, it's potentially convenient to be able to implement a single keyword recognition algorithm for modifiers on all declarations, and in that approach it's impossible to distinguish between a constructor declaration or a method declaration spelled value foo() {} (that is, unless the algorithm is willing to rely on name resolution to decide whether foo is the current class name). So we've tentatively added value to the list of names excluded from TypeIdentifier.

3.9 Keywords

51 character sequences, formed from ASCII characters, are reserved for use as keywords and cannot be used as identifiers (3.8). Another 17 18 character sequences, also formed from ASCII characters, may be interpreted as keywords or as other tokens, depending on the context in which they appear.

Keyword:
ReservedKeyword
ContextualKeyword
ReservedKeyword:
(one of)
abstract continue for new switch
assert default if package synchronized
boolean do goto private this
break double implements protected throw
byte else import public throws
case enum instanceof return transient
catch extends int short try
char final interface static void
class finally long strictfp volatile
const float native super while
_ (underscore)
ContextualKeyword:
(one of)
exports opens requires uses yield
module permits sealed var
non-sealed provides to when
open record transitive with
value

The keywords const and goto are reserved, even though they are not currently used. This may allow a Java compiler to produce better error messages if these C++ keywords incorrectly appear in programs.

The keyword strictfp is obsolete and should not be used in new code.

The keyword _ (underscore) may be used in certain declarations in place of an identifier (6.1).

true and false are not keywords, but rather boolean literals (3.10.3).

null is not a keyword, but rather the null literal (3.10.8).

During the reduction of input characters to input elements (3.5), a sequence of input characters that notionally matches a contextual keyword is reduced to a contextual keyword if and only if both of the following conditions hold:

  1. The sequence is recognized as a terminal specified in a suitable context of the syntactic grammar (2.3), as follows:

    • For module, when recognized as a terminal in a SingleModuleImportDeclaration (7.5.5), or a ModuleDeclaration (7.7).

    • For open, when recognized as a terminal in a ModuleDeclaration (7.7).

    • For exports, opens, provides, requires, to, uses, and with, when recognized as a terminal in a ModuleDirective.

    • For transitive, when recognized as a terminal in a RequiresModifier.

      For example, recognizing the sequence requires transitive ; does not make use of RequiresModifier, so the term transitive is reduced here to an identifier and not a contextual keyword.

    • For var, when recognized as a terminal in a LocalVariableType (14.4) or a LambdaParameterType (15.27.1).

      In other contexts, attempting to use var as an identifier will cause an error, because var is not a TypeIdentifier (3.8).

    • For yield, when recognized as a terminal in a YieldStatement (14.21).

      In other contexts, attempting to use the yield as an identifier will cause an error, because yield is neither a TypeIdentifier nor a UnqualifiedMethodIdentifier.

    • For record, when recognized as a terminal in a RecordDeclaration (8.10).

    • For permits, when recognized as a terminal in a ClassPermits (8.1.6) or an InterfacePermits (9.1.4).

    • For non-sealed, permits, and sealed, when recognized as a terminal ClassModifier or InterfaceModifier in a NormalClassDeclaration (8.1), EnumDeclaration (8.9), RecordDeclaration (8.10), or a NormalInterfaceDeclaration (9.1), or AnnotationInterfaceDeclaration (9.6).

    • For when, when recognized as a terminal in a Guard (14.11.1).

    • For value, when recognized as a ClassModifier in a NormalClassDeclaration, EnumDeclaration, or RecordDeclaration.

    Some combinations of modifiers and declarations don't make sense, like a value enum. However, these are semantic restrictions that should not affect parsing.

  2. The sequence is not immediately preceded or immediately followed by an input character that matches JavaLetterOrDigit.

In general, accidentally omitting white space in source code will cause a sequence of input characters to be tokenized as an identifier, due to the "longest possible translation" rule (3.2). For example, the sequence of twelve input characters p u b l i c s t a t i c is always tokenized as the identifier publicstatic, rather than as the reserved keywords public and static. If two tokens are intended, they must be separated by white space or a comment.

The rule above works in tandem with the "longest possible translation" rule to produce an intuitive result in contexts where contextual keywords may appear. For example, the sequence of eleven input characters v a r f i l e n a m e is usually tokenized as the identifier varfilename, but in a local variable declaration, the first three input characters are tentatively recognized as the contextual keyword var by the first condition of the rule above. However, it would be confusing to overlook the lack of white space in the sequence by recognizing the next eight input characters as the identifier filename. (This would mean that the sequence undergoes different tokenization in different contexts: an identifier in most contexts, but a contextual keyword and an identifier in local variable declarations.) Accordingly, the second condition prevents recognition of the contextual keyword var on the grounds that the immediately following input character f is a JavaLetterOrDigit. The sequence v a r f i l e n a m e is therefore tokenized as the identifier varfilename in a local variable declaration.

As another example of the careful recognition of contextual keywords, consider the sequence of 15 input characters n o n - s e a l e d c l a s s. This sequence is usually translated to three tokens - the identifier non, the operator -, and the identifier sealedclass - but in a normal class declaration, where the first condition holds, the first ten input characters are tentatively recognized as the contextual keyword non-sealed. To avoid translating the sequence to two keyword tokens (non-sealed and class) rather than three non-keyword tokens, and to avoid rewarding the programmer for omitting white space before class, the second condition prevents recognition of the contextual keyword. The sequence n o n - s e a l e d c l a s s is therefore tokenized as three tokens in a class declaration.

In the rule above, the first condition depends on details of the syntactic grammar, but a compiler for the Java programming language can implement the rule without fully parsing the input program. For example, a heuristic could be used to track the contextual state of the tokenizer, as long as the heuristic guarantees that valid uses of contextual keywords are tokenized as keywords, and valid uses of identifiers are tokenized as identifiers. Alternatively, a compiler could always tokenize a contextual keyword as an identifier, leaving it to a later phase to recognize special uses of these identifiers.

Chapter 4: Types, Values, and Variables

4.3 Reference Types and Values

4.3.1 Objects

An object is a class instance or an array. A class instance may be an identity object or a value object; every array is an identity object.

The reference values (often just references) are pointers to these objects, and a special null reference, which refers to no object.

A class instance is explicitly created by a class instance creation expression (15.9).

An array is explicitly created by an array creation expression (15.10.1).

Other expressions may implicitly create a class instance (12.5) or an array (10.6).

Creation of an identity object produces a unique object, distinct from any that has previously been created. Creation of a value object may result in an object that is indistinguishable from a previously-created object.

Example 4.3.1-1. Object Creation

class Point {
    int x, y;
    Point() { System.out.println("default"); }
    Point(int x, int y) { this.x = x; this.y = y; }

    /* A Point instance is explicitly created at
       class initialization time: */
    static Point origin = new Point(0,0);

    /* A String can be implicitly created
       by a + operator: */
    public String toString() { return "(" + x + "," + y + ")"; }
}

class Test {
    public static void main(String[] args) {
        /* A Point is explicitly created
           using newInstance: */
        Point p = null;
        try {
            p = (Point)Class.forName("Point").newInstance();
        } catch (Exception e) {
            System.out.println(e);
        }

        /* An array is implicitly created
           by an array initializer: */
        Point[] a = { new Point(0,0), new Point(1,1) };

        /* Strings are implicitly created
           by + operators: */
        System.out.println("p: " + p);
        System.out.println("a: { " + a[0] + ", " + a[1] + " }");

        /* An array is explicitly created
           by an array creation expression: */
        String[] sa = new String[2];
        sa[0] = "he"; sa[1] = "llo";
        System.out.println(sa[0] + sa[1]);
    }
}

This program produces the output:

default
p: (0,0)
a: { (0,0), (1,1) }
hello

Programs work with objects via reference values. Reference values (often just references) refer to objects, or may be the special null reference, which refers to no object.

The operators on references to objects are:

There may be many references to the same object. Most objects Identity objects often have mutable state, stored in the fields of objects that are instances of classes or in the variables that are the components of an array object. If two variables contain references to the same identity object, the state of the object can be modified using one variable's reference to the object, and then the altered state can be observed through the reference in the other variable.

Example 4.3.1-2. Primitive and Reference Identity

class Value { int val; }

class Test {
    public static void main(String[] args) {
        int i1 = 3;
        int i2 = i1;
        i2 = 4;
        System.out.print("i1==" + i1);
        System.out.println(" but i2==" + i2);
        Value v1 = new Value();
        v1.val = 5;
        Value v2 = v1;
        v2.val = 6;
        System.out.print("v1.val==" + v1.val);
        System.out.println(" and v2.val==" + v2.val);
    }
}

This program produces the output:

i1==3 but i2==4
v1.val==6 and v2.val==6

because v1.val and v2.val reference the same instance variable (4.12.3) in the one Value object created by the only new expression, while i1 and i2 are different variables.

These examples need to be updated.

There are a lot of problems in this second example that will need to be cleaned up. For example, the word "value" is confusing here.

Each identity object is associated with a monitor (17.1), which is used by synchronized methods (8.4.3) and the synchronized statement (14.19) to provide control over concurrent access to state by multiple threads (17).

Chapter 5: Conversions and Contexts

5.1 Kinds of Conversion

5.1.7 Boxing Conversion

Boxing conversion treats expressions of a primitive type as expressions of a corresponding reference type. Specifically, the following nine conversions are called the boxing conversions:

At run time, boxing conversion proceeds as follows:

If the value p being boxed is the result of evaluating a constant expression (15.29) of type boolean, byte, char, short, int, or long, and the result is true, false, a character in the range '\u0000' to '\u007f' inclusive, or an integer in the range -128 to 127 inclusive, then let a and b be the results of any two boxing conversions of p. It is always the case that a == b.

Ideally, boxing a primitive value would always yield an identical reference. In practice, this may not be feasible using existing implementation techniques. The rule above is a pragmatic compromise, requiring that certain common values always be boxed into indistinguishable objects. The implementation may cache these, lazily or eagerly. For other values, the rule disallows any assumptions about the identity of the boxed values on the programmer's part. This allows (but does not require) sharing of some or all of these references.

This ensures that in most common cases, the behavior will be the desired one, without imposing an undue performance penalty, especially on small devices. Less memory-limited implementations might, for example, cache all char and short values, as well as int and long values in the range of -32K to +32K.

If a and b are variables storing the results of any two boxing conversions of p, then it is always the case that a.equals(b) and a == b.

A boxing conversion may result in an OutOfMemoryError if a new instance of one of the wrapper classes (Boolean, Byte, Character, Short, Integer, Long, Float, or Double) needs to be allocated and insufficient storage is available.

Chapter 6: Names

6.5 Determining the Meaning of a Name

6.5.6 Meaning of Expression Names

6.5.6.1 Simple Expression Names

If an expression name consists of a single Identifier, then:

If the declaration denotes an instance variable of a class C (8.3.1.1), then all of the following must be true, or a compile-time error occurs:

For example, the expression name must not appear in the body of a static method declared by C, nor in the body of an instance method of a static class nested within C.

...

These changes allow the declared instance fields of C to be both read and written in an early construction context (except where the field has an initializer that hasn't run yet). JVM bytecode does not allow a larval object's fields to be read during early construction, so as necessary the compiler tracks field values locally in the generated bytecode.

This includes a source-incompatible change preventing field assignments before alternate constructor invocations, as discussed by JDK-8368719.

Chapter 8: Classes

8.1 Class Declarations

8.1.1 Class Modifiers

A class declaration may include class modifiers.

ClassModifier:
(one of)
Annotation public protected private
abstract static final sealed non-sealed strictfp
value

The rules concerning annotation modifiers for a class declaration are specified in 9.7.4 and 9.7.5.

The access modifier public (6.6) pertains only to top level classes (7.6) and member classes (8.5, 9.5), not to local classes (14.3) or anonymous classes (15.9.5).

The access modifiers protected and private pertain only to member classes.

The modifier static pertains only to member classes and local classes.

It is a compile-time error if the same keyword appears more than once as a modifier for a class declaration, or if a class declaration has more than one of the access modifiers public, protected, and private.

It is a compile-time error if a class declaration has more than one of the modifiers sealed, non-sealed, and final.

If two or more (distinct) class modifiers appear in a class declaration, then it is customary, though not required, that they appear in the order consistent with that shown above in the production for ClassModifier.

8.1.1.2 sealed, non-sealed, and final Classes

A class can be declared sealed if all its direct subclasses are known when the class is declared (8.1.6), and no other direct subclasses are desired or required.

Explicit and exhaustive control over a class's direct subclasses is useful when the class hierarchy is used to model the kinds of values in a domain, rather than as a mechanism for code inheritance and reuse. The direct subclasses may themselves be declared sealed in order to further control the class hierarchy.

A class can be declared final if its definition is complete and no subclasses are desired or required.

It is a compile-time error if a class is declared both final and abstract, because the implementation of such a class could never be completed (8.1.1.1).

Because a final class never has any subclasses, the methods of a final class are never overridden (8.4.8.1).

A class is freely extensible if its direct superclass is not sealed (8.1.4), and none of its direct superinterfaces are sealed (8.1.5), and it is neither sealed nor final itself.

A class that has a sealed direct superclass or a sealed direct superinterface is freely extensible if and only if it is declared non-sealed.

It is a compile-time error if a class has a sealed direct superclass or a sealed direct superinterface, and is not declared final, sealed, or non-sealed either explicitly or implicitly.

Thus, an effect of the sealed keyword is to force all direct subclasses to explicitly declare whether they are final, sealed, or non-sealed. This avoids accidentally exposing a sealed class hierarchy to unwanted subclassing.

An enum class is either implicitly final or implicitly sealed, so it can implement a sealed interface. Similarly, a record class is record classes and non-abstract value classes are implicitly final, so it they can also implement a sealed interface.

It is a compile-time error if a class is declared non-sealed but has neither a sealed direct superclass nor a sealed direct superinterface.

Thus, a subclass of a non-sealed class cannot itself be declared non-sealed.

8.1.1.5 value Classes

The value modifier specifies that a class does not depend on object identity to support unique instance creation, instance field mutation, or synchronization (4.3.1). All instances of a non-abstract value class are value objects.

A class without the value modifier is an identity class, and may depend on features associated with object identity. All instances of an identity class are identity objects. A value class may not extend an identity class, with the exception of the identity class Object (8.1.4).

If a value class is not abstract, it is implicitly final (8.1.1.2). It is permitted for the class declaration to redundantly specify the final modifier.

It is a compile-time error if a value class is not abstract but is declared with the modifier sealed or non-sealed.

Special restrictions apply to the field declarations (8.3.1.2), method declarations (8.4.3.6), and constructors (8.8.7) of a value class.

Previous iterations of this specification required an abstract value class to be stateless and have an empty constructor. This is no longer the case: an abstract value class may declare instance fields, may be an inner class, and may have instance initialization logic.

8.1.4 Superclasses and Subclasses

The optional extends clause in a normal class declaration specifies the direct superclass type of the class being declared.

ClassExtends:
extends ClassType

The extends clause must not appear in the definition of the class Object, or a compile-time error occurs, because it is the primordial class and has no direct superclass type.

The ClassType must name an accessible class (6.6), or a compile-time error occurs.

It is a compile-time error if the ClassType names a class that is sealed (8.1.1.2) and the class being declared is not a permitted direct subclass of the named class (8.1.6).

It is a compile-time error if the ClassType names a class that is final, because final classes are not allowed to have subclasses (8.1.1.2).

It is a compile-time error if the ClassType names the class Enum, which can only be extended by an enum class (8.9), or names the class Record, which can only be extended by a record class (8.10).

In a value class, it is a compile-time error if the ClassType names an identity class (8.1.1.5) other than the class Object.

If the ClassType has type arguments, it must denote a well-formed parameterized type (4.5), and none of the type arguments may be wildcard type arguments, or a compile-time error occurs.

The direct superclass type of a class whose declaration lacks an extends clause is as follows:

The direct superclass of a class is the class named by its direct superclass type. The direct superclass is important because its implementation is used to derive the implementation of the class being declared.

...

8.3 Field Declarations

8.3.1 Field Modifiers

8.3.1.2 final Fields

A field can be declared final (4.12.4). Both class and instance variables (static and non-static fields) may be declared final.

In a value class, every non-static field is implicitly final. It is permitted for the field declaration to redundantly specify the final modifier.

A blank final class variable must be definitely assigned by a static initializer of the class in which it is declared, or a compile-time error occurs (8.7, 16.8).

A blank final instance variable must be definitely assigned and moreover not definitely unassigned at the end of every constructor of the class in which it is declared, or a compile-time error occurs (8.8, 16.9).

Furthermore, when declared by a value class or a record class, a blank final instance variable must be definitely assigned after the argument list of every explicit superclass constructor invocation (8.8.7.1) in the class, or a compile-time error occurs.

8.3.2 Field Initialization

If a declarator in a field declaration has a variable initializer, then the declarator has the semantics of an assignment (15.26) to the declared variable.

If the declarator is for a class variable (that is, a static field) (8.3.1.1), then the following rules apply to its initializer: the initializer occurs in a static context (8.1.3); references to the class variable being declared or forward references to other class variables are restricted, according to the rules in 8.3.3. At run time, the initializer is evaluated and the assignment performed exactly once, when the class is initialized (12.4.2).

Note that static fields that are constant variables (4.12.4) are initialized before other static fields (12.4.2, step 6). This also applies in interfaces (9.3.1). When such fields are referenced by simple name, they will never be observed to have their default initial values (4.12.5).

If the declarator is for an instance variable (that is, a field that is not static), then the following rules apply to its initializer: the initializer may refer to any class variable of the class, even one whose declaration occurs to the right of the initializer; references to the instance variable being declared or forward references to other instance variables are restricted, according to the rules in 8.3.3. At run time, the initializer is evaluated and the assignment performed each time an instance of the class is created (12.5).

If the declarator is for an instance variable of a value class (8.1.1.5), then the initializer is an early instance variable initializer and is evaluated at the start of each constructor that contains a superclass constructor invocation (either explicit or implicit). The initializer occurs in an early construction context (8.8.7), which restricts references to the current object using this and super (15.8.3, 15.11.2), as well as unqualified references to instance methods (6.5.7.1) and certain unqualified references to instance variables (6.5.6.1).

If the declarator is for an instance variable of an identity class, then it is a late instance variable initializer and is evaluated immediately after each superclass constructor invocation in the class (either explicit or implicit). The initializer does not occur in an early construction context; it may freely refer to the current object using this and super, and may freely access the object's instance methods and instance variables.

References from variable initializers to fields that may not yet be initialized are restricted, as specified in 8.3.3 and 16.

Exception checking for a variable initializer in a field declaration is specified in 11.2.3.

Variable initializers are also used in local variable declaration statements (14.4), where the initializer is evaluated and the assignment performed each time the local variable declaration statement is executed.

Example 8.3.2-1. Field Initialization

class Point {
    int x = 1, y = 5;
}
class Test {
    public static void main(String[] args) {
        Point p = new Point();
        System.out.println(p.x + ", " + p.y);
    }
}

This program produces the output:

1, 5

because the assignments to x and y occur whenever a new Point is created.

Example 8.3.2-2. Forward Reference to a Class Variable

class Test {
    float f = j;
    static int j = 1;
}

This program compiles without error; it initializes j to 1 when class Test is initialized, and initializes f to the current value of j every time an instance of class Test is created.

8.4 Method Declarations

8.4.3 Method Modifiers

8.4.3.6 synchronized Methods

A synchronized method acquires a monitor (17.1) before it executes.

For a class (static) method, the monitor associated with the Class object for the method's class is used.

For an instance method, the monitor associated with this (the object for which the method was invoked) is used.

It is a compile-time error for a value class to declare a synchronized instance method.

...

8.8 Constructor Declarations

8.8.7 Constructor Body

A constructor body is a block of code that is executed as part of the process of creating a new instance of a class (12.5). A constructor body may contain an explicit invocation of another constructor of the same class or of the direct superclass (8.8.7.1).

ConstructorBody:
{ [BlockStatements] ConstructorInvocation [BlockStatements] }
{ [BlockStatements] }

If a constructor body contains an explicit constructor invocation, the BlockStatements preceding the constructor invocation are called the prologue of the constructor body. The prologue of a constructor body may be empty. The BlockStatements in a constructor with no explicit constructor invocation and the BlockStatements following a constructor invocation in a constructor body are called the epilogue. The epilogue of a constructor body may also be empty.

A construct (statement, local variable declaration statement, local class declaration, local interface declaration, or expression) occurs in the early construction context of a class C if it is contained in either the prologue of a constructor body of C, or it is nested in the constructor invocation (8.8.7.1) of a constructor body of C, or an early instance field initializer (8.3.2) of C. References to the current object using this and super are restricted in an early construction context (15.8.3, 15.11.2), as are unqualified references to instance methods (6.5.7.1) and certain unqualified references to instance variabes (6.5.6.1).

If the body of a constructor in a value class (8.1.1.5) or a record class (8.10) does not contain an explicit constructor invocation, then the BlockStatements constitute the prologue and the epilogue is empty. The constructor body implicitly ends with a superclass constructor invocation "super();", an invocation of the constructor of the direct superclass that takes no arguments.

If a constructor body the body of a constructor in an any other class does not contain an explicit constructor invocation, then the prologue is empty and the BlockStatements constitute the epilogue. and If the constructor being declared is not part of the primordial class Object, then the constructor body (i) has an empty prologue, (ii) implicitly begins with a superclass constructor invocation "super();", an implicit invocation of the constructor of the direct superclass that takes no arguments, and (iii) the statements, if any, given in the constructor body are taken to form the epilogue of the constructor body.

Except for the possibility of explicit or implicit constructor invocations, and the prohibition on return statements (14.17), the body of a constructor is like the body of a method (8.4.7).

Note that a constructor body contains at most one constructor invocation. The grammar makes it impossible, for example, to place constructor invocations in different branches of an if statement.

Example 8.8.7-1. Constructor Bodies

class Point {
    int x, y;
    Point(int x, int y) { this.x = x; this.y = y; }
}
class ColoredPoint extends Point {
    static final int WHITE = 0, BLACK = 1;
    int color;
    ColoredPoint(int x, int y) {
        this(x, y, WHITE);
    }
    ColoredPoint(int x, int y, int color) {
        super(x, y);
        this.color = color;
    }
}

Here, the first constructor of ColoredPoint invokes the second, providing an additional argument; the second constructor of ColoredPoint invokes the constructor of its superclass Point, passing along the coordinates.

8.8.7.1 Constructor Invocations
ConstructorInvocation:
[TypeArguments] this ( [ArgumentList] ) ;
[TypeArguments] super ( [ArgumentList] ) ;
ExpressionName . [TypeArguments] super ( [ArgumentList] ) ;
Primary . [TypeArguments] super ( [ArgumentList] ) ;

The following productions from 4.5.1 and 15.12 are shown here for convenience:

TypeArguments:
< TypeArgumentList >
ArgumentList:
Expression {, Expression}

Constructor invocations are divided into two kinds:

It is a compile-time error for a constructor to directly or indirectly invoke itself through a series of one or more alternate constructor invocations.

A constructor invocation introduces an early construction context (8.8.7), which limits the use of constructs that refer to the current object. Notably, references to the current object using this and super are restricted in an early construction context (15.8.3, 15.11.2), as are references to instance variables (6.5.6.1) and instance methods (6.5.7.1).

If TypeArguments is present to the left of this or super, then it is a compile-time error if any of the type arguments are wildcards (4.5.1).

Let C be the class being instantiated, and let S be the direct superclass of C.

If a superclass constructor invocation is unqualified, then:

If a superclass constructor invocation is qualified, then:

The exception types that an explicit constructor invocation can throw are specified in 11.2.2.

...

8.9 Enum Classes

An enum declaration specifies a new enum class, a restricted kind of class that defines a small set of named class instances.

EnumDeclaration:
{ClassModifier} enum TypeIdentifier [ClassImplements] EnumBody

An enum declaration may specify a top level enum class (7.6), a member enum class (8.5, 9.5), or a local enum class (14.3).

The TypeIdentifier in an enum declaration specifies the name of the enum class.

It is a compile-time error if an enum declaration has the modifier abstract, final, sealed, or non-sealed, or value.

An enum class is either implicitly final or implicitly sealed, as follows:

A nested enum class is implicitly static. That is, every member enum class and local enum class is static. It is permitted for the declaration of a member enum class to redundantly specify the static modifier, but it is not permitted for the declaration of a local enum class (14.3).

It is a compile-time error if the same keyword appears more than once as a modifier for an enum declaration, or if an enum declaration has more than one of the access modifiers public, protected, and private (6.6).

The direct superclass type of an enum class E is Enum<E> (8.1.4).

An enum declaration does not have an extends clause, so it is not possible to explicitly declare a direct superclass type, even Enum<E>.

An enum class has no instances other than those defined by its enum constants. It is a compile-time error to attempt to explicitly instantiate an enum class (15.9.1).

In addition to the compile-time error, three further mechanisms ensure that no instances of an enum class exist beyond those defined by its enum constants:

8.10 Record Classes

A record declaration specifies a new record class, a restricted kind of class that defines a simple aggregate of values.

RecordDeclaration:
{ClassModifier} record TypeIdentifier [TypeParameters] RecordHeader
[ClassImplements] RecordBody

A record declaration may specify a top level record class (7.6), a member record class (8.5, 9.5), or a local record class (14.3).

The TypeIdentifier in a record declaration specifies the name of the record class.

It is a compile-time error if a record declaration has the modifier abstract, sealed, or non-sealed.

A record class is implicitly final. It is permitted for the declaration of a record class to redundantly specify the final modifier.

A nested record class is implicitly static. That is, every member record class and local record class is static. It is permitted for the declaration of a member record class to redundantly specify the static modifier, but it is not permitted for the declaration of a local record class (14.3).

It is a compile-time error if the same keyword appears more than once as a modifier for a record declaration, or if a record declaration has more than one of the access modifiers public, protected, and private (6.6).

A record class may be a value class or an identity class (8.1.1.5).

A record class is often a good candidate to be a value class, because its instance fields are always final and its implicitly declared equals method makes no use of identity (8.10.3).

The direct superclass type of a record class is Record (8.1.4).

A record declaration does not have an extends clause, so it is not possible to explicitly declare a direct superclass type, even Record.

The serialization mechanism treats instances of a record class differently than ordinary serializable or externalizable objects. In particular, a record object is deserialized using the canonical constructor (8.10.4).

8.10.4 Record Constructor Declarations

8.10.4.1 Normal Canonical Constructors

A (non-compact) constructor in the declaration of record class R is the canonical constructor of R if its signature is override-equivalent (8.4.2) to the derived constructor signature of R.

The derived constructor signature of a record class R is a signature that consists of the name R, no type parameters, and the formal parameter types derived from the record header of R by taking the declared type of each record component in order.

As a canonical constructor has a signature that is override-equivalent to the derived constructor signature of the record class, there can be only one canonical constructor declared explicitly in the record class.

The declaration of a (non-compact) canonical constructor must satisfy all of the following conditions, or a compile-time error occurs:

A consequence of these rules is that the annotations on a record component can differ from the annotations on the corresponding formal parameter of an explicitly declared canonical constructor. For example, the following record declaration is valid:

import java.lang.annotation.Target;
import java.lang.annotation.ElementType;

@interface Foo {}
@interface Bar {}

record Person(@Foo String name) {
    Person(@Bar String name) {
        this.name = name;
    }
}

This change allows an explicit super() in the canonical constructor of a record class. Normally, all of a record class's canonical constructor code appears in an early construction context (8.8.7). A developer may wish to use an explicit super() invocation in rare cases where some of the construction logic needs to refer to this. Doing so is fine as long as, per 8.3.1.2, all of the record class's fields are set before super() is invoked.

8.10.4.2 Compact Canonical Constructors

A compact constructor declaration is a succinct form of constructor declaration, only available in a record declaration. It declares the canonical constructor of a record class without requiring the record components of the class to be manually repeated as formal parameters of the constructor.

CompactConstructorDeclaration:
{ConstructorModifier} SimpleTypeName ConstructorBody

The following productions from 8.8, 8.8.3, and 8.8.7 are shown here for convenience:

ConstructorModifier:
(one of)
Annotation public protected private
SimpleTypeName:
TypeIdentifier
ConstructorBody:
{ [BlockStatements] ConstructorInvocation [BlockStatements] }
{ [BlockStatements] }

It is a compile-time error for a record declaration to have more than one compact constructor declaration.

The formal parameters of a compact constructor of a record class are implicitly declared. They are given by the derived formal parameter list of the record class (8.10.4).

The compact constructor of a record class is a variable arity constructor (8.8.1) if the record class has a variable arity record component.

The signature of a compact constructor declaration is equal to the derived constructor signature of the record class (8.10.4.1).

The body of a compact constructor declaration must satisfy all of the following conditions, or a compile-time error occurs:

The body of a compact constructor occurs in an early construction context (8.8.7), which restricts references to the current object using this and super (15.8.3, 15.11.2), as well as unqualified references to instance methods (6.5.7.1) and certain unqualified references to instance variables (6.5.6.1).

If a record declaration has a record component named c, then the simple name c in the body of a compact constructor denotes the implicit formal parameter named c, and not the component field named c.

After the last statement, if any, in the body of the compact constructor has completed normally (14.1), all component fields of the record class are implicitly initialized to the values of the corresponding formal parameters. The component fields are initialized in the order that the corresponding record components are declared in the record header.

The intent of a compact constructor declaration is that only code to validate or normalize parameters needs to be given in the constructor body; the remaining initialization code is supplied by the compiler. For example, the following record class has a compact constructor that simplifies a rational number:

record Rational(int num, int denom) {
    private static int gcd(int a, int b) {
        if (b == 0) return Math.abs(a);
        else return gcd(b, a % b);
    }

    Rational {
        int gcd = gcd(num, denom);
        num    /= gcd;
        denom  /= gcd;
    }
}

The compact constructor Rational {...} behaves the same as this normal constructor:

Rational(int num, int denom) {
    int gcd = gcd(num, denom);
    num    /= gcd;
    denom  /= gcd;
    this.num   = num;
    this.denom = denom;
    super();
}

Chapter 12: Execution

12.5 Creation of New Class Instances

A new class instance is explicitly created when evaluation of a class instance creation expression (15.9) causes a class to be instantiated.

A new class instance may be implicitly created in the following situations:

Each of these situations identifies a particular constructor (8.8) to be called with specified arguments (possibly none) as part of the class instance creation process.

Whenever a new class instance is created, memory space is allocated for it with room for all the instance variables declared in the class and all the instance variables declared in each superclass of the class, including all the instance variables that may be hidden (8.3).

If there is not sufficient space available to allocate memory for the object, then creation of the class instance completes abruptly with an OutOfMemoryError. Otherwise, all the instance variables in the new object, including those declared in superclasses, are initialized to their default values (4.12.5).

Just before a reference to the newly created object is returned as the result, the indicated constructor is processed to initialize the new object using the following procedure:

  1. Assign the arguments for the constructor to newly created parameter variables for this constructor invocation.

  2. If this constructor does not contain an explicit constructor invocation (8.8.7.1) then continue from step 5. If this constructor is declared in a value class (8.1.1.5) and does not contain an alternate constructor invocation (8.8.7.1), execute the early instance variable initializers for this class (8.3.2), assigning the values of the initializers to the corresponding instance variables, in the left-to-right order in which they appear textually in the source code of the class. If execution of any of these initializers results in an exception, then no further initializers are processed and this procedure completes abruptly with that same exception, otherwise continue with the next step.

  3. Execute the BlockStatements, if any, of the prologue of the constructor body. If execution of any statement completes abruptly, then execution of the constructor completes abruptly for the same reason, otherwise continue with the next step.

  4. If this constructor contains an explicit constructor invocation, The explicit constructor the invocation is either an invocation of another constructor in the same class (using this) or an invocation of a superclass constructor (using super). Evaluate the arguments of the constructor invocation and process the constructor invocation recursively using these same seven steps. If the constructor invocation completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, continue from step 7 if the invocation is of another constructor in the same class, and continue from step 6 if the invocation is of a superclass constructor.

  5. If this constructor contains no explicit constructor invocation and is for a class other than Object, then this constructor contains an implicit invocation of a superclass constructor with no arguments. In this case, process the implicit constructor invocation recursively using these same seven steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason, otherwise continue with the next step.

  6. Execute the instance initializers and late instance variable initializers for this class, assigning the values of instance variable initializers to the corresponding instance variables, in the left-to-right order in which they appear textually in the source code for the class. If execution of any of these initializers results in an exception, then no further initializers are processed and this procedure completes abruptly with that same exception, otherwise continue with the next step.

    In a value class, the instance variable initializers are all early initializers and were already executed in step 2.

  7. Execute the BlockStatements, if any, of the epilogue of this constructor. If execution of any statement completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, this procedure completes normally.

Unlike C++, the Java programming language does not specify altered rules for method dispatch during the creation of a new class instance. If methods are invoked that are overridden in subclasses in the object being initialized, then these overriding methods are used, even before the new object is completely initialized. Classes can avoid unwanted exposure of uninitialized state by assigning to their fields in the prologue of the constructor body.

Example 12.5-1. Evaluation of Instance Creation

class Point {
    int x, y;
    Point() { x = 1; y = 1; }
}
class ColoredPoint extends Point {
    int color = 0xFF00FF;
}
class Test {
    public static void main(String[] args) {
        ColoredPoint cp = new ColoredPoint();
        System.out.println(cp.color);
    }
}

Here, a new instance of ColoredPoint is created. First, space is allocated for the new ColoredPoint, to hold the fields x, y, and color. All these fields are then initialized to their default values (in this case, 0 for each field). Next, the ColoredPoint constructor with no arguments is first invoked. Since ColoredPoint declares no constructors, a default constructor of the following form is implicitly declared:

ColoredPoint() { super(); }

This constructor then invokes the Point constructor with no arguments. The Point constructor does not begin with an invocation of a constructor, so the Java compiler provides an implicit invocation of its superclass constructor of no arguments, as though it had been written:

Point() { super(); x = 1; y = 1; }

Therefore, the constructor for Object which takes no arguments is invoked.

The class Object has no superclass, so the recursion terminates here. Next, any instance initializers and instance variable initializers of Object are invoked. Next, the body of the constructor of Object that takes no arguments is executed. No such constructor is declared in Object, so the Java compiler supplies a default one, which in this special case is:

Object() { }

This constructor executes without effect and returns.

Next, all initializers for the instance variables of class Point are executed. As it happens, the declarations of x and y do not provide any initialization expressions, so no action is required for this step of the example. Then the body of the Point constructor is executed, setting x to 1 and y to 1.

Next, the initializers for the instance variables of class ColoredPoint are executed. This step assigns the value 0xFF00FF to color. Finally, the epilogue of the ColoredPoint constructor is executed (the part after the invocation of super); there happen to be no statements in the epilogue, so no further action is required and initialization is complete.

Example 12.5-2. Dynamic Dispatch During Instance Creation

class Super {
    Super() { printThree(); }
    void printThree() { System.out.println("three"); }
}
class Test extends Super {
    int three = (int)Math.PI;  // That is, 3
    void printThree() { System.out.println(three); }

    public static void main(String[] args) {
        Test t = new Test();
        t.printThree();
    }
}

This program produces the output:

0
3

This shows that the invocation of printThree in the constructor for class Super does not invoke the definition of printThree in class Super, but rather invokes the overriding definition of printThree in class Test. This method therefore runs before the field initializers of Test have been executed, which is why the first value output is 0, the default value to which the field three of Test is initialized. The later invocation of printThree in method main invokes the same definition of printThree, but by that point the initializer for instance variable three has been executed, and so the value 3 is printed.

Example 12.5-3. Initialization of Fields in the Prologue

class Super {
    Super() { printThree(); }
    void printThree() { System.out.println("three"); }
}
class Test extends Super {
    int three;

    public Test() {
        three = (int)Math.PI;  // That is, 3
        super();
    }

    void printThree() { System.out.println(three); }

    public static void main(String[] args) {
        Test t = new Test();
        t.printThree();
    }
}

This alternative to Example 12.5-2 produces the output:

3
3

Because the field three is initialized in the prologue of the Test constructor, its assignment occurs in Step 3 of the object initialization procedure, before execution of the Super constructor in Step 4. When three is initialized in this way, it is impossible to observe it with the default value 0.

Chapter 13: Binary Compatibility

13.4 Evolution of Classes

This section describes the effects of changes to the declaration of a class and its members and constructors on pre-existing binaries.

13.4.1 abstract and value Classes

If a class that was not declared abstract is changed to be declared abstract, then pre-existing binaries that attempt to create new instances of that class will throw either an InstantiationError at link time, or (if a reflective method is used) an InstantiationException at run time; such a change is therefore not recommended for widely distributed classes.

Changing a an identity class that is declared abstract to no longer be declared abstract does not break compatibility with pre-existing binaries.

Removing the abstract modifier from a value class declaration has the side-effect of making the class final, with binary compatibility risks outlined in 13.4.2.3.

Modifying an abstract or final identity class to be a value class does not break compatibility with pre-existing binaries.

Adding the value modifier to a non-abstract, non-final class declaration has the side-effect of making the class final, with binary compatibility risks outlined in 13.4.2.3.

Removing the value modifier from a non-abstract value class does not break compatibility with pre-existing binaries.

Removing the value modifier from an abstract value class causes an IncompatibleClassChangeError to be thrown whenever a binary of a pre-existing value subclass of the class is loaded, because value classes cannot extend identity classes (8.1.4); such a change is not recommended for widely distributed classes.

Aside from binary compatibility risks, changing an identity class to be a value class, or vice versa, will change instances' behavior with respect to indentity-sensitive operations like == (15.21.3) and synchronized (14.19), and may affect the timing of initializer and constructor logic (12.5).

Chapter 14: Blocks, Statements, and Patterns

14.19 The synchronized Statement

A synchronized statement acquires a mutual-exclusion lock (17.1) on behalf of the executing thread, executes a block, then releases the lock. While the executing thread owns the lock, no other thread may acquire the lock.

SynchronizedStatement:
synchronized ( Expression ) Block

The type of Expression must be a reference type, and must not be a final value class type, or a type variable or intersection type bounded by a final value class type, or a compile-time error occurs.

A synchronized statement is executed by first evaluating the Expression. Then:

The locks acquired by synchronized statements are the same as the locks that are acquired implicitly by synchronized methods (8.4.3.6). A single thread may acquire a lock more than once.

Acquiring the lock associated with an identity object does not in itself prevent other threads from accessing fields of the object or invoking un-synchronized methods on the object. Other threads can also use synchronized methods or the synchronized statement in a conventional manner to achieve mutual exclusion.

Example 14.19-1. The synchronized Statement

class Test {
    public static void main(String[] args) {
        Test t = new Test();
        synchronized(t) {
            synchronized(t) {
                System.out.println("made it!");
            }
        }
    }
}

This program produces the output:

made it!

Note that this program would deadlock if a single thread were not permitted to lock a monitor more than once.

Chapter 15: Expressions

15.8 Primary Expressions

15.8.3 this

The keyword this may be used as an expression in the following contexts:

When used as an expression, the keyword this denotes a value that is a reference either to the object for which the instance method was invoked (15.12), or to the object being constructed. The value denoted by this in a lambda body (15.27.2) is the same as the value denoted by this in the surrounding context.

The keyword this is also used in constructor invocations (8.8.7.1), and to denote the receiver parameter of a method or constructor (8.4).

It is a compile-time error if a this expression occurs in a static context (8.1.3).

Let C be the innermost enclosing class or interface declaration of a this expression.

It is a compile-time error if a this expression occurs in an early construction context (8.8.7) of C, unless it appears as the qualifier of a field access expression (15.11) that is the left-hand operand of a simple assignment expression (15.26), and the simple assignment field access expression is not enclosed in a lambda expression or inner class declaration that is contained in the early construction context of C.

If C is generic, with type parameters F1,...,Fn, the type of this is C<F1,...,Fn>. Otherwise, the type of this is C.

At run time, the class of the actual object referred to may be C or a subclass of C (8.1.5).

Example 15.8.3-1. The this Expression

class IntVector {
    int[] v;
    boolean equals(IntVector other) {
        if (this == other)
            return true;
        if (v.length != other.v.length)
            return false;
        for (int i = 0; i < v.length; i++) {
            if (v[i] != other.v[i]) return false;
        }
        return true;
    }
}

Here, the class IntVector implements a method equals, which compares two vectors. If the other vector is the same vector object as the one for which the equals method was invoked, then the check can skip the length and value comparisons. The equals method implements this check by comparing the reference to the other object to this.

15.8.4 Qualified this

Any lexically enclosing instance (8.1.3) can be referred to by explicitly qualifying the keyword this.

Let n be an integer such that TypeName denotes the n'th lexically enclosing class or interface declaration of the class or interface whose declaration immediately encloses the qualified this expression.

The value of a qualified this expression TypeName.this is the n'th lexically enclosing instance of this.

If TypeName denotes a generic class, with type parameters F1,...,Fn, the type of the qualified this expression is TypeName<F1,...,Fn>. Otherwise, the type of the qualified this expression is TypeName.

It is a compile-time error if a qualified this expression occurs in a static context (8.1.3).

It is a compile-time error if a qualified this expression occurs in an early construction context (8.8.7) of the class named by TypeName, unless it appears as the qualifier of a field access expression (15.11) that is the left-hand operand of a simple assignment expression (15.26), and the simple assignment field access expression is not enclosed in a lambda expression or inner class declaration that is contained in the early construction context of the class named by TypeName.

It is a compile-time error if the class or interface whose declaration immediately encloses a qualified this expression is not an inner class of TypeName or TypeName itself.

15.9 Class Instance Creation Expressions

15.9.4 Run-Time Evaluation of Class Instance Creation Expressions

At run time, evaluation of a class instance creation expression is as follows.

First, if the class instance creation expression is a qualified class instance creation expression, the qualifying primary expression is evaluated. If the qualifying expression evaluates to null, a NullPointerException is raised, and the class instance creation expression completes abruptly. If the qualifying expression completes abruptly, the class instance creation expression completes abruptly for the same reason.

Next, space is allocated for the new class instance. If there is insufficient space to allocate the object, evaluation of the class instance creation expression completes abruptly by throwing an OutOfMemoryError.

The new object contains new instances of all the fields declared in the specified class and all its superclasses. As each new field instance is created, it is initialized to its default value (4.12.5).

Next, the actual arguments to the constructor are evaluated, left-to-right. If any of the argument evaluations completes abruptly, any argument expressions to its right are not evaluated, and the class instance creation expression completes abruptly for the same reason.

Next, the selected constructor of the specified class is invoked. This results in invoking at least one constructor for each superclass of the class. This process can be directed by explicit constructor invocations (8.8.7.1) and is specified in detail in 12.5.

The value of a class instance creation expression is a reference to the newly created object of the specified class. Every time If the specified class is an identity class, then every time the expression is evaluated, a fresh object is created the object that is created has a unique identity.

Example 15.9.4-1. Evaluation Order and Out-Of-Memory Detection

If evaluation of a class instance creation expression finds there is insufficient memory to perform the creation operation, then an OutOfMemoryError is thrown. This check occurs before any argument expressions are evaluated.

So, for example, the test program:

class List {
    int value;
    List next;
    static List head = new List(0);
    List(int n) { value = n; next = head; head = this; }
}
class Test {
    public static void main(String[] args) {
        int id = 0, oldid = 0;
        try {
            for (;;) {
                ++id;
                new List(oldid = id);
            }
        } catch (Error e) {
            List.head = null;
            System.out.println(e.getClass() + ", " + (oldid==id));
        }
    }
}

prints:

class java.lang.OutOfMemoryError, false

because the out-of-memory condition is detected before the argument expression oldid = id is evaluated.

Compare this to the treatment of array creation expressions, for which the out-of-memory condition is detected after evaluation of the dimension expressions (15.10.2).

15.9.5 Anonymous Class Declarations

An anonymous class is implicitly declared by a class instance creation expression or by an enum constant that ends with a class body (8.9.1).

An anonymous class is never abstract (8.1.1.1).

An anonymous class is never sealed (8.1.1.2), and thus has no permitted direct subclasses (8.1.6).

An anonymous class declared by a class instance creation expression is never final (8.1.1.2).

An anonymous class declared by an enum constant is always final.

An anonymous class being non-final is relevant in casting, in particular the narrowing reference conversion allowed for the cast operator (5.5). On the other hand, it is not relevant to subclassing, because it is impossible to declare a subclass of an anonymous class (an anonymous class cannot be named by an extends clause) despite the anonymous class being non-final.

An anonymous class is always an identity class (8.1.1.5) and an inner class (8.1.3).

Like a local class or interface (14.3), an anonymous class is not a member of any package, class, or interface (7.1, 8.5).

The direct superclass type or direct superinterface type of an anonymous class declared by a class instance creation expression is given by the expression (15.9.1), with type arguments inferred as necessary while choosing a constructor (15.9.3). If a direct superinterface type is given, the direct superclass type is Object.

The direct superclass type of an anonymous class declared by an enum constant is the type of the declaring enum class.

The ClassBody of the class instance creation expression or enum constant declares fields (8.3), methods (8.4), member classes (8.5), member interfaces (9.1.1.3), instance initializers (8.6), and static initializers (8.7) of the anonymous class. The constructor of an anonymous class is always implicit (15.9.5.1).

If a class instance creation expression with a ClassBody uses a diamond (<>) for the type arguments of the class to be instantiated, then for all non-private methods declared in the ClassBody, it is as if the method declaration is annotated with @Override (9.6.4.4).

When <> is used, the inferred type arguments may not be as anticipated by the programmer. Consequently, the supertype of the anonymous class may not be as anticipated, and methods declared in the anonymous class may not override supertype methods as intended. Treating such methods as if annotated with @Override (if they are not explicitly annotated with @Override) helps avoid silently incorrect programs.

15.21 Equality Operators

15.21.3 Reference Object Equality Operators == and !=

If the operands of an equality operator are both of either reference type or the null type, then the operation is object equality.

It is a compile-time error if it is impossible to convert the type of either operand to the type of the other by a casting conversion (5.5). The run-time values of the two operands would necessarily be unequal (ignoring the case where both values are null).

At run time, the result of == is true if the operand values are both null, or both refer to the same identity object or array, or both refer to statewise-equivalent value objects; otherwise, the result is false.

Two value objects are statewise equivalent if they are instances of the same class and the values of their instance fields are the same. For primitive-typed instance fields, this means the field values are identical (in the case of a floating-point type, all bits of the value are compared, as if by first applying Float.floatToRawIntBits or Double.doubleToRawLongBits). For reference-typed instance fields, this means the field values are ==, possibly applying this definition recursively.

The result of != is false if the operand values are both null, or both refer to the same identity object or array, or both refer to statewise-equivalent value objects; otherwise, the result is true.

While == may be used to compare references of type String, such an equality test determines whether or not the two operands refer to the same String object. The result is false if the operands are distinct String objects, even if they contain the same sequence of characters (3.10.5, 3.10.6). The contents of two strings s and t can be tested for equality by the method invocation s.equals(t).

A common mistake is to use the == operator to test for the same identity object or statewise-equivalent value objects, when what is usually wanted is more abstract: testing whether two objects represent the same value or entity. The equals method serves this purpose, allowing classes to declare an appropriate domain-specific notion of equality.

For example, two distinct String objects, s and t, may contain the same sequence of characters. A comparison of these objects using s == t will evaluate to false, while in most cases the program ought to use s.equals(t) to compare the character sequences that the objects represent.

Chapter 16: Definite Assignment

Every local variable declared by a statement (14.4.2, 14.14.1, 14.14.2, 14.20.3) and every blank final field (4.12.4, 8.3.1.2) must have a definitely assigned value when any access of its value occurs.

An access to its value consists of the simple name of the variable (or, for a field, the simple name of the field qualified by this) occurring anywhere in an expression except as the left-hand operand of the simple assignment operator = (15.26.1).

For every access of a local variable declared by a statement x, or blank final field x, x must be definitely assigned before the access, or a compile-time error occurs.

Similarly, every blank final variable must be assigned at most once; it must be definitely unassigned when an assignment to it occurs.

Such an assignment is defined to occur if and only if either the simple name of the variable (or, for a field, its simple name qualified by this) occurs on the left hand side of an assignment operator.

For every assignment to a blank final variable, the variable must be definitely unassigned before the assignment, or a compile-time error occurs.

Similarly, for every alternate constructor invocation (8.8.7.1) occurring in a constructor of a class C, every blank final instance variable of C declared in C must be definitely unassigned after the argument list of the alternate constructor invocation, or a compile-time error occurs.

This rule is no longer necessary with the changes to 6.5.6.1. (See also JDK-8368719.)

Note that local variables declared by a pattern (14.30) are not subject to the rules of definite assignment. Every local variable declared by a pattern is initialized by the process of pattern matching and so always has a value when accessed.

...

16.9 Definite Assignment, Constructors, and Instance Initializers

Let C be a class declared within the scope of V. Then:

Let C be a class, and let V be a blank final non-static member field of C, declared in C. Then:

Let C be a class, and let V be a blank final non-static member field of C, declared in a superclass of C. Then:

Let C be a class, and let V be a blank final static member field of C. Then:

Let C be a class, and let V be a local variable declared by a statement S contained by a constructor or instance variable initializer of C. Then:

The following rules hold within the constructors (8.8.7) of class C:

Chapter 17: Threads and Locks

17.1 Synchronization

The Java programming language provides multiple mechanisms for communicating between threads. The most basic of these methods is synchronization, which is implemented using monitors. Each identity object in Java is associated with a monitor, which a thread can lock or unlock. Only one thread at a time may hold a lock on a monitor. Any other threads attempting to lock that monitor are blocked until they can obtain a lock on that monitor. A thread t may lock a particular monitor multiple times; each unlock reverses the effect of one lock operation.

The synchronized statement (14.19) computes a reference to an object; it then attempts to perform a lock action on that object's monitor and does not proceed further until the lock action has successfully completed. After the lock action has been performed, the body of the synchronized statement is executed. If execution of the body is ever completed, either normally or abruptly, an unlock action is automatically performed on that same monitor.

A synchronized method (8.4.3.6) automatically performs a lock action when it is invoked; its body is not executed until the lock action has successfully completed. If the method is an instance method, it locks the monitor associated with the instance for which it was invoked (that is, the object that will be known as this during execution of the body of the method). If the method is static, it locks the monitor associated with the Class object that represents the class in which the method is defined. If execution of the method's body is ever completed, either normally or abruptly, an unlock action is automatically performed on that same monitor.

The Java programming language neither prevents nor requires detection of deadlock conditions. Programs where threads hold (directly or indirectly) locks on multiple objects should use conventional techniques for deadlock avoidance, creating higher-level locking primitives that do not deadlock, if necessary.

Other mechanisms, such as reads and writes of volatile variables and the use of classes in the java.util.concurrent package, provide alternative ways of synchronization.

17.2 Wait Sets and Notification

Every identity object, in addition to having an associated monitor, has an associated wait set. A wait set is a set of threads.

When an identity object is first created, its wait set is empty. Elementary actions that add threads to and remove threads from wait sets are atomic. Wait sets are manipulated solely through the methods Object.wait, Object.notify, and Object.notifyAll.

Wait set manipulations can also be affected by the interruption status of a thread, and by the Thread class's methods dealing with interruption. Additionally, the Thread class's methods for sleeping and joining other threads have properties derived from those of wait and notification actions.

17.5 final Field Semantics

17.5.1 Semantics of final Fields

Let o be an object, and c be a constructor for o in which a final field f is written. A freeze action on final field f of o takes place when c exits, either normally or abruptly. as follows:

The JLS is concerned with the semantics of Java programs, so we do not address here other kinds of strict fields (both final and non-final) that can only be expressed in bytecode. But the intent is that the timing of a freeze action for one of these fields would be the same as in a value class.

Note that if one constructor invokes another constructor, and the invoked constructor sets a final field, the freeze for the final field takes place at the end of the invoked constructor.

For each execution, the behavior of reads is influenced by two additional partial orders, the dereference chain dereferences() and the memory chain mc(), which are considered to be part of the execution (and thus, fixed for any particular execution). These partial orders must satisfy the following constraints (which need not have a unique solution):

Given a write w, a freeze f, an action a (that is not a read of a final field), a read r1 of the final field frozen by f, and a read r2 such that hb(w, f), hb(f, a), mc(a, r1), and dereferences(r1, r2), then when determining which values can be seen by r2, we consider hb(w, r2). (This happens-before ordering does not transitively close with other happens-before orderings.)

Note that the dereferences order is reflexive, and r1 can be the same as r2.

For reads of final fields, the only writes that are deemed to come before the read of the final field are the ones derived through the final field semantics.

17.5.3 Subsequent Modification of final Fields

In some cases, such as deserialization, the system will need to change the final fields of an object after construction. The final instance fields of a non-record identity class can be changed via reflection and other implementation-dependent means. The only pattern in which this has reasonable semantics is one in which an object is constructed and then the final fields of the object are updated. The object should not be made visible to other threads, nor should the final fields be read, until all updates to the final fields of the object are complete. Freezes of a final field occur both at the end of the constructor in which the final field is set, and immediately after each modification of a final field via reflection or other special mechanism.

Even then, there are a number of complications. If a final field is initialized to a constant expression (15.29) in the field declaration, changes to the final field may not be observed, since uses of that final field are replaced at compile time with the value of the constant expression.

Another problem is that the specification allows aggressive optimization of final fields. Within a thread, it is permissible to reorder reads of a final field with those modifications of a final field that do not take place in the constructor.

Example 17.5.3-1. Aggressive Optimization of final Fields

class A {
    final int x;
    A() {
        x = 1;
    }

    int f() {
        return d(this,this);
    }

    int d(A a1, A a2) {
        int i = a1.x;
        g(a1);
        int j = a2.x;
        return j - i;
    }

    static void g(A a) {
        // uses reflection to change a.x to 2
    }
}

In the d method, the compiler is allowed to reorder the reads of x and the call to g freely. Thus, new A().f() could return -1, 0, or 1.

...