This specification is not final and is subject to change. Use is subject to license terms.

Value Objects

Changes to the Java® Language Specification • Version 20-internal-adhoc.dlsmith.20220830

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

A companion documents describes JVM changes.

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, identity, 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, identity, 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 and identity keywords (along with sealed) without disallowing these as class names.

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 and identity 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 16 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
module permits sealed var
non-sealed provides to with
open record transitive yield
value identity

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) is reserved for possible future use in parameter declarations.

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 and 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, value, and identity, 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).

    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 the same as a previously-created object.

Two value objects are the same if they are instances of the same class and the values of their instance fields are the same. For fields of a floating-point primitive type, the field values are considered the same if the results of the appropriate Float.floatToIntBits or Double.doubleToLongBits method are the same. For fields of a reference type, the field values are considered the same if they are both null, or if they are references to the same object (possibly applying this definition recursively).

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) are pointers to objects, and a 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 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
identity 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.

It is a compile-time error if a class declaration has both of the modifiers identity and value.

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.5 identity and value Classes

The identity modifier specifies that all instances of a class (15.9.4) and its subclasses (8.1.4) are identity objects (4.3.1). The value modifier specifies that all instances of a class and its subclasses are value objects.

A class with neither the modifier identity nor value is implicitly an identity class if it has any of the following properties:

Thus, every class—with the special exception of Object—is either an identity class, a value class, or an abstract class that can support both kinds of subclasses.

To avoid confusion, an abstract class that declares no fields but that is implicitly an identity class should, as a matter of style, be explicitly declared an identity class.

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.

The identity and value modifiers limit the set of classes that can extend a non-final class (8.1.4).

Special restrictions apply to the field declarations (8.3.1), method declarations (8.4.3.6), and constructors (8.8.7) of a class that is not an identity class.

For a discussion of how we expect typical abstract classes to be impacted by these rules, see this email.

There is no way for an abstract class to assert that it is intended to be compatible with both value and identity subclasses. Should there be? (Compare the @FunctionalInterface annotation.)

8.1.3 Inner Classes and Enclosing Instances

...

An inner class C is a direct inner class of a class or interface O if O is the immediately enclosing class or interface declaration of C and the declaration of C does not occur in a static context.

If an inner class is a local class or an anonymous class, it may be declared in a static context, and in that case is not considered an inner class of any enclosing class or interface.

A class C is an inner class of class or interface O if it is either a direct inner class of O or an inner class of an inner class of O.

It is unusual, but possible, for the immediately enclosing class or interface declaration of an inner class to be an interface. This only occurs if the class is a local or anonymous class declared in a default or static method body (9.4).

A class or interface O is the zeroth lexically enclosing class or interface declaration of itself.

A class O is the n'th lexically enclosing class declaration of a class C if it is the immediately enclosing class declaration of the n-1'th lexically enclosing class declaration of C.

An instance i of a direct inner class C of a class or interface O is associated with an instance of O, known as the immediately enclosing instance of i. The immediately enclosing instance of an object, if any, is determined when the object is created (15.9.2).

An object o is the zeroth lexically enclosing instance of itself.

An object o is the n'th lexically enclosing instance of an instance i if it is the immediately enclosing instance of the n-1'th lexically enclosing instance of i.

An instance of an inner local class or an anonymous class whose declaration occurs in a static context has no immediately enclosing instance. Also, an instance of a static nested class (8.1.1.4) has no immediately enclosing instance.

It is a compile-time error if an inner class has an immediately enclosing instance but is declared an abstract value class (8.1.1.1, 8.1.1.5).

If an abstract class is declared with neither the value nor the identity modifier, but it is an inner class and has an immediately enclosing instance, it is implicitly an identity class, per 8.1.1.5.

...

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).

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.

The superclass relationship is the transitive closure of the direct superclass relationship. A class A is a superclass of class C if either of the following is true:

A class is said to be a direct subclass of its direct superclass, and a subclass of each of its superclasses.

It is a compile-time error if an identity class (8.1.1.5) has a superclass that is a value class, or if a value class has a superclass that is an identity class.

...

8.1.5 Superinterfaces

The optional implements clause in a class declaration specifies the direct superinterface types of the class being declared.

ClassImplements:
implements InterfaceTypeList
InterfaceTypeList:
InterfaceType {, InterfaceType}

Each InterfaceType must name an accessible interface (6.6), or a compile-time error occurs.

It is a compile-time error if any InterfaceType names a interface that is sealed (9.1.1.4) and the class being declared is not a permitted direct subclass of the named interface (9.1.4).

If an InterfaceType 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.

It is a compile-time error if the same interface is named by a direct superinterface type more than once in a single implements clause. This is true even if the interface is named in different ways.

Example 8.1.5-1. Illegal Superinterfaces

class Redundant implements java.lang.Cloneable, Cloneable {
    int x;
}

This program results in a compile-time error because the names java.lang.Cloneable and Cloneable refer to the same interface.

A class whose declaration lacks an implements clause has no direct superinterface types, with one exception: an anonymous class may have a superinterface type (15.9.5).

An interface is a direct superinterface of a class if the interface is named by one of the direct superinterface types of the class.

An interface I is a superinterface of class C if any of the following is true:

A class can have a superinterface in more than one way.

A class is said to directly implement its direct superinterfaces, and to implement all of its superinterfaces.

A class is said to be a direct subclass of its direct superinterfaces, and a subclass of all of its superinterfaces.

It is a compile-time error if an identity class (8.1.1.5) has a superinterface that is a value interface, or if a value class has a superinterface that is an identity interface, or if any class has both an identity superclass or superinterface and a value superclass or superinterface.

A class may not declare a direct superclass type and a direct superinterface type, or two direct superinterface types, which are, or which have supertypes (4.10.2) which are, different parameterizations of the same generic interface (9.1.2), or a parameterization of a generic interface and a raw type naming that same generic interface. In the case of such a conflict, a compile-time error occurs.

This requirement was introduced in order to support translation by type erasure (4.6).

...

8.3 Field Declarations

8.3.1 Field Modifiers

8.3.1.1 static Fields

If a field is declared static, there exists exactly one incarnation of the field, no matter how many instances (possibly zero) of the class may eventually be created. A static field, sometimes called a class variable, is incarnated when the class is initialized (12.4).

A field that is not declared static is called an instance variable, and sometimes called a non-static field. Whenever a new instance of a class is created (12.5), a new variable associated with that instance is created for every instance variable declared in that class or any of its superclasses.

It is a compile-time error for an abstract value class to declare a non-static field.

If an abstract class is declared with neither the value nor the identity modifier, but it declares a non-static field, it is implicitly an identity class, per 8.1.1.5.

The declaration of a class variable introduces a static context (8.1.3), which limits the use of constructs that refer to the current object. Notably, the keywords this and super are prohibited in a static context (15.8.3, 15.11.2), as are unqualified references to instance variables, instance methods, and type parameters of lexically enclosing declarations (6.5.5.1, 6.5.6.1, 15.12.3).

References to an instance variable from a static context or a nested class or interface are restricted, as specified in 6.5.6.1.

...

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).

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.

If an abstract class is declared with neither the value nor the identity modifier, but it declares a synchronized instance method, it is implicitly an identity class, per 8.1.1.5.

...

8.6 Instance Initializers

An instance initializer declared in a class is executed when an instance of the class is created (12.5, 15.9, 8.8.7.1).

InstanceInitializer:
Block

It is a compile-time error for an abstract value class to declare an instance initializer.

If an abstract class is declared with neither the value nor the identity modifier, but it declares an instance initializer, it is implicitly an identity class, per 8.1.1.5.

It is a compile-time error if an instance initializer cannot complete normally (14.22).

It is a compile-time error if a return statement (14.17) appears anywhere within an instance initializer.

An instance initializer is permitted to refer to the current object using the keyword this (15.8.3) or the keyword super (15.11.2, 15.12), and to use any type variables in scope.

Restrictions on how an instance initializer may refer to instance variables, even when the instance variables are in scope, are specified in 8.3.3.

Exception checking for an instance initializer is specified in 11.2.3.

8.8 Constructor Declarations

A constructor is used in the creation of an object that is an instance of a class (12.5, 15.9).

ConstructorDeclaration:
{ConstructorModifier} ConstructorDeclarator [Throws] ConstructorBody
ConstructorDeclarator:
[TypeParameters] SimpleTypeName
( [ReceiverParameter ,] [FormalParameterList] )
SimpleTypeName:
TypeIdentifier

The rules in this section apply to constructors in all class declarations, including enum declarations and record declarations. However, special rules apply to enum declarations with regard to constructor modifiers, constructor bodies, and default constructors; these rules are stated in 8.9.2. Special rules also apply to record declarations with regard to constructors, as stated in 8.10.4.

The SimpleTypeName in the ConstructorDeclarator must be the simple name of the class that contains the constructor declaration, or a compile-time error occurs.

In all other respects, a constructor declaration looks just like a method declaration that has no result (8.4.5).

Constructor declarations are not members. They are never inherited and therefore are not subject to hiding or overriding.

It is a compile-time error for an abstract value class to declare a nontrivial constructor (8.1.1.5).

If an abstract class is declared with neither the value nor the identity modifier, but it declares a nontrivial constructor, it is implicitly an identity class, per 8.1.1.5.

It's not ideal to define a new term just for the purpose of this rule. But the list of things to check is long, and we don't want to repeat it. Perhaps it would be helpful to somehow overlap this definition with the discussion of default constructors in 8.8.9.

Constructors are invoked by class instance creation expressions (15.9), by the conversions and concatenations caused by the string concatenation operator + (15.18.1), and by explicit constructor invocations from other constructors (8.8.7). Access to constructors is governed by access modifiers (6.6), so it is possible to prevent class instantiation by declaring an inaccessible constructor (8.8.10).

Constructors are never invoked by method invocation expressions (15.12).

Example 8.8-1. Constructor Declarations

class Point {
    int x, y;
    Point(int x, int y) { this.x = x; this.y = y; }
}

8.8.7 Constructor Body

The first statement of a constructor body may be an explicit invocation of another constructor of the same class or of the direct superclass (8.8.7.1).

ConstructorBody:
{ [ExplicitConstructorInvocation] [BlockStatements] }

It is a compile-time error for the constructor of a non-abstract value class to invoke a constructor of the direct superclass.

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

If a constructor body does not begin with an explicit constructor invocation and the constructor being declared is not part of the primordial class Object or a non-abstract value class, then the constructor body implicitly begins with a superclass constructor invocation "super();", an invocation of the constructor of its direct superclass that takes no arguments.

Except for the possibility of explicit constructor invocations, and the prohibition on explicitly returning a value (14.17), the body of a constructor is like the body of a method (8.4.7).

A return statement (14.17) may be used in the body of a constructor if it does not include an expression.

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.9 Default Constructor

If a class contains no constructor declarations, then a default constructor is implicitly declared. The form of the default constructor for a top level class, member class, or local class is as follows:

The form of the default constructor for an anonymous class is specified in 15.9.5.1.

It is a compile-time error if a default constructor is implicitly declared but the superclass does not have an accessible constructor that takes no arguments and has no throws clause.

Example 8.8.9-1. Default Constructors

The declaration:

public class Point {
    int x, y;
}

is equivalent to the declaration:

public class Point {
    int x, y;
    public Point() { super(); }
}

where the default constructor is public because the class Point is public.

Example 8.8.9-2. Accessibility of Constructors v. Classes

The rule that the default constructor of a class has the same accessibility as the class itself is simple and intuitive. Note, however, that this does not imply that the constructor is accessible whenever the class is accessible. Consider:

package p1;
public class Outer {
    protected class Inner {}
}
package p2;
class SonOfOuter extends p1.Outer {
    void foo() {
        new Inner();  // compile-time access error
    }
}

The default constructor for Inner is protected. However, the constructor is protected relative to Inner, while Inner is protected relative to Outer. So, Inner is accessible in SonOfOuter, since it is a subclass of Outer. Inner's constructor is not accessible in SonOfOuter, because the class SonOfOuter is not a subclass of Inner! Hence, even though Inner is accessible, its default constructor is not.

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:

An enum class is implicitly an identity class. It is permitted for the declaration of an enum class to redundantly specify the identity modifier.

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 an identity class or a value class (8.1.1.5). It is a compile-time error if a record declaration has both of the modifiers identity and value. A record declaration with neither the identity nor the value modifier is implicitly an identity class.

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.1 Record Components

...

It is a compile-time error for a record declaration to declare a record component with the name clone, finalize, getClass, hashCode, isValueObject, notify, notifyAll, toString, or wait.

These are the names of the no-args public and protected methods in Object. Disallowing them as the names of record components avoids confusion in a number of ways. First, every record class provides implementations of hashCode and toString that return representations of a record object as a whole; they cannot serve as accessor methods (8.10.3) for record components called hashCode or toString, and there would be no way to access such record components from outside the record class. Similarly, some record classes may provide implementations of clone and (regrettably) finalize, so a record component called clone or finalize could not be accessed via an accessor method. Finally, the getClass, notify, notifyAll, and wait methods in Object are final, so record components with the same names could not have accessor methods. (The accessor methods would have the same signatures as the final methods, and would thus attempt, unsuccessfully, to override them.)

...

Chapter 9: Interfaces

9.1 Interface Declarations

9.1.1 Interface Modifiers

An interface declaration may include interface modifiers.

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

The rules concerning annotation modifiers for an interface declaration are specified in 9.7.4 and 9.7.5.

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

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

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

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

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

It is a compile-time error if an interface declaration has both of the modifiers identity and value.

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

9.1.1.5 identity and value Interfaces

The identity modifier specifies that all instances of subclasses of an interface (8.1.5) are identity objects (4.3.1). The value modifier specifies that all instances of subclasses of an interface are value objects.

By default, an interface is neither an identity interface nor a value interface, and may have instances of both kinds of objects.

The identity and value modifiers limit the set of classes and interfaces that can implement or extend an interface (8.1.5, 9.1.3).

9.1.3 Superinterfaces and Subinterfaces

If an extends clause is provided, then the interface being declared extends each of the specified interface types and therefore inherits the member classes, member interfaces, instance methods, and static fields of each of those interface types.

The specified interface types are the direct superinterface types of the interface being declared.

Any class that implements the declared interface is also considered to implement all the interfaces that this interface extends.

InterfaceExtends:
extends InterfaceTypeList

The following production from 8.1.5 is shown here for convenience:

InterfaceTypeList:
InterfaceType {, InterfaceType}

Each InterfaceType in the extends clause of an interface declaration must name an accessible interface (6.6), or a compile-time error occurs.

It is a compile-time error if any InterfaceType names a interface that is sealed (9.1.1.4) and the interface being declared is not a permitted direct subinterface of the named interface (9.1.4).

If an InterfaceType 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.

One interface is a direct superinterface of another interface if the first interface is named by one of the direct superinterface types of the second interface.

The superinterface relationship is the transitive closure of the direct superinterface relationship. An interface I is a superinterface of interface K if either of the following is true:

An interface is said to be a direct subinterface of its direct superinterface, and a subinterface of each of its superinterfaces.

While every class is an extension of class Object, there is no single interface of which all interfaces are extensions.

It is a compile-time error if an identity interface (8.1.1.5) has a superinterface that is a value interface, or if a value interface has a superinterface that is an identity interface, or if any interface has both an identity superinterface and a value superinterface.

An interface I directly depends on a class or interface A if A is mentioned in the extends clause of I either as a superinterface or as a qualifier in the fully qualified form of a superinterface name.

An interface I depends on a class or interface A if any of the following is true:

It is a compile-time error if an interface depends on itself.

If circularly declared interfaces are detected at run time, as interfaces are loaded, then a ClassCircularityError is thrown (12.2.1).

9.6 Annotation Interfaces

An annotation interface declaration specifies an annotation interface, a specialized kind of interface. To distinguish an annotation interface declaration from a normal interface declaration, the keyword interface is preceded by an at sign (@).

AnnotationInterfaceDeclaration:
{InterfaceModifier} @ interface TypeIdentifier AnnotationInterfaceBody

Note that the at sign (@) and the keyword interface are distinct tokens. It is possible to separate them with whitespace, but this is discouraged as a matter of style.

Unless explicitly modified in this section and its subsections, all of the rules that apply to normal interface declarations (9.1) apply to annotation interface declarations.

For example, annotation interface declarations have the same rules for scope as normal interface declarations.

It is a compile-time error if an annotation interface declaration has the modifier sealed, or non-sealed, identity, or value (9.1.1.4).

An annotation interface declaration may specify a top level interface or a member interface, but not a local interface (14.3).

An annotation interface declaration is not permitted syntactically to appear within a block, by virtue of the LocalClassOrInterfaceDeclaration production in 14.3.

It is a compile-time error if an annotation interface declaration appears directly or indirectly in the body of a local class, local interface, or anonymous class declaration (14.3, 15.9.5).

This rule, together with the syntactic restriction on annotation interface declarations noted above, ensures that an annotation interface always has a canonical name (6.7). Having such a name is important because the purpose of an annotation interface is to be used by annotations in other compilation units. Since a local class or interface does not have a canonical name, an annotation interface declared anywhere within its syntactic body (if that were allowed) would not have a canonical name either.

...

9.8 Functional Interfaces

A functional interface is an interface that is not declared with one of the modifiers sealed, identity, or value, and has just one abstract method (aside from the methods of Object), and thus represents a single function contract. This "single" method may take the form of multiple abstract methods with override-equivalent signatures inherited from superinterfaces; in this case, the inherited methods logically represent a single method.

For an interface I that is not declared with one of the modifiers sealed, identity, or value, let M be the set of abstract methods that are members of I that do not have the same signature as any public instance method of the class Object (4.3.2). Then, I is a functional interface if there exists a method m in M for which both of the following are true:

In addition to the usual process of creating an interface instance by declaring and instantiating a class (15.9), instances of functional interfaces can be created with method reference expressions and lambda expressions (15.13, 15.27).

The definition of functional interface excludes methods in an interface that are also public methods in Object. This is to allow functional treatment of an interface like java.util.Comparator<T> that declares multiple abstract methods of which only one is really "new" - int compare(T,T). The other - boolean equals(Object) - is an explicit declaration of an abstract method that would otherwise be implicitly declared in the interface (9.2) and automatically implemented by every class that implements the interface.

Note that if non-public methods of Object, such as clone(), are explicitly declared in an interface as public, they are not automatically implemented by every class that implements the interface. The implementation inherited from Object is protected while the interface method is public, so the only way to implement the interface would be for a class to override the non-public Object method with a public method.

...

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, identity, 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 class that is declared abstract to no longer be declared abstract does not break compatibility with may impact pre-existing binaries, depending on the identity and value modifiers:

If one of the identity or value modifiers is added to an abstract class, a pre-existing binary that attempts to extend the class may fail to load. Specifically, if the subclass has an incompatible identity or value modifier, or implements an interface with an incompatible identity or value modifier, class loading will fail with an IncompatibleClassChangeError.

Removing one of the identity or value modifiers from an abstract class does not break compatibility with pre-existing binaries.

Many abstract classes are implicitly identity classes (8.1.1.5). In that case, adding or removing the explicit identity modifier has no effect, but other changes to the class declaration may have the effect of removing the implicit identity modifier. For stability, developers are encouraged to explicitly declare such classes as identity classes.

Modifying a non-abstract identity class to be a value class is a binary compatible change, as long as the class is already final and all its constructors are private. If the class is not final, declaring it a value class has the effect of declaring the class final (13.4.2.3). If the class has a non-private constructor, pre-existing binaries that attempt to invoke that constructor will throw an InstantiationError at link time.

Modifying a non-abstract value class to be an identity class is a binary compatible change, as long as all of the class's constructors are private. If the class has a non-private constructor, pre-existing binaries that attempt to invoke that constructor will behave as if the constructor had been removed (13.4.12).

13.4.7 Access to Members and Constructors

Changing the declared access of a member or constructor to permit less access may break compatibility with pre-existing binaries, causing a linkage error to be thrown when these binaries are resolved. Less access is permitted if the access modifier is changed from package access to private access; from protected access to package or private access; or from public access to protected, package, or private access. Changing a member or constructor to permit less access is therefore not recommended for widely distributed classes.

Changing the declared access of the constructor of an abstract class that is not an identity class to permit less access may also have the side-effect of making the class an identity class (8.1.1.5), with binary compatibility risks outlined in 13.4.1.

Perhaps surprisingly, the binary format is defined so that changing a member or constructor to be more accessible does not cause a linkage error when a subclass (already) defines a method to have less access.

Example 13.4.7-1. Changing Accessibility

If the package points defines the class Point:

package points;
public class Point {
    public int x, y;
    protected void print() {
        System.out.println("(" + x + "," + y + ")");
    }
}

used by the program:

class Test extends points.Point {
    public static void main(String[] args) {
        Test t = new Test();
        t.print();
    }
    protected void print() { 
        System.out.println("Test"); 
    }
}

then these classes compile and Test executes to produce the output:

Test

If the method print in class Point is changed to be public, and then only the Point class is recompiled, and then executed with the previously existing binary for Test, then no linkage error occurs. This happens even though it is improper, at compile time, for a public method to be overridden by a protected method (as shown by the fact that the class Test could not be recompiled using this new Point class unless print in Test were changed to be public.)

Allowing superclasses to change protected methods to be public without breaking binaries of pre-existing subclasses helps make binaries less fragile. The alternative, where such a change would cause a linkage error, would create additional binary incompatibilities.

13.4.8 Field Declarations

Widely distributed programs should not expose any fields to their clients. Apart from the binary compatibility issues discussed below, this is generally good software engineering practice. Adding a field to a class may break compatibility with pre-existing binaries that are not recompiled.

Assume a reference to a field f with qualifying class C. Assume further that f is in fact an instance (respectively static) field declared in a superclass of C, S, and that the type of f is X.

If a new field of type X with the same name as f is added to a subclass of S that is a superclass of C or C itself, then a linkage error may occur. Such a linkage error will occur only if, in addition to the above, either one of the following is true:

In particular, no linkage error will occur in the case where a class could no longer be recompiled because a field access previously referenced a field of a superclass with an incompatible type. The previously compiled class with such a reference will continue to reference the field declared in a superclass.

Adding an instance field to an abstract class that is not an identity class also has the side-effect of making the class an identity class (8.1.1.5), with binary compatibility risks outlined in 13.4.1.

Example 13.4.8-1. Adding A Field Declaration

class Hyper { String h = "hyper"; }
class Super extends Hyper { String s = "super"; }
class Test {
    public static void main(String[] args) {
        System.out.println(new Super().h);
    }
}

This program produces the output:

hyper

Suppose a new version of class Super is produced:

class Super extends Hyper {
    String s = "super";
    int h = 0;
}

Then, recompiling Hyper and Super, and executing the resulting new binaries with the old binary of Test produces the output:

hyper

The field h of Hyper is output by the original binary of Test. While this may seem surprising at first, it serves to reduce the number of incompatibilities that occur at run time. (In an ideal world, all source files that needed recompilation would be recompiled whenever any one of them changed, eliminating such surprises. But such a mass recompilation is often impractical or impossible, especially in the Internet. And, as was previously noted, such recompilation would sometimes require further changes to the source code.)

As another example, if the program:

class Hyper { String h = "Hyper"; }
class Super extends Hyper { }
class Test extends Super {
    public static void main(String[] args) {
        String s = new Test().h;
        System.out.println(s);
    }
}

is compiled and executed, it produces the output:

Hyper

Suppose that a new version of class Super is then compiled:

class Super extends Hyper { char h = 'h'; }

If the resulting binary is used with the existing binaries for Hyper and Test, then the output is still:

Hyper

even though compiling the source for these binaries:

class Hyper { String h = "Hyper"; }
class Super extends Hyper { char h = 'h'; }
class Test extends Super {
    public static void main(String[] args) {
        String s = new Test().h;
        System.out.println(s);
    }
}

would result in a compile-time error, because the h in the source code for main would now be construed as referring to the char field declared in Super, and a char value can't be assigned to a String.

Deleting a field from a class will break compatibility with any pre-existing binaries that reference this field, and a NoSuchFieldError will be thrown when such a reference from a pre-existing binary is linked. Only private fields may be safely deleted from a widely distributed class.

For purposes of binary compatibility, adding or deleting a field f whose type involves type variables (4.4) or parameterized types (4.5) is equivalent to the addition (respectively, deletion) of a field of the same name whose type is the erasure (4.6) of the type of f.

13.4.10 static Fields

If a field that is not declared private was not declared static and is changed to be declared static, or vice versa, then a linkage error, specifically an IncompatibleClassChangeError, will result if the field is used by a pre-existing binary which expected a field of the other kind. Such changes are not recommended in code that has been widely distributed.

Removing a static modifier from a field of an abstract class that is not an identity class also has the side-effect of making the class an identity class (8.1.1.5), with binary compatibility risks outlined in 13.4.1.

13.4.12 Method and Constructor Declarations

Adding a method or constructor to a class will not break compatibility with any pre-existing binaries, even in the case where a class could no longer be recompiled because an invocation previously referenced a method or constructor of a superclass with an incompatible type. The previously compiled class with such a reference will continue to reference the method or constructor declared in a superclass.

Assume a reference to a method m with qualifying class C. Assume further that m is in fact an instance (respectively static) method declared in a superclass of C, S.

If a new method of type X with the same signature and return type as m is added to a subclass of S that is a superclass of C or C itself, then a linkage error may occur. Such a linkage error will occur only if, in addition to the above, either one of the following is true:

Adding a synchronized method or a nontrivial constructor to an abstract class that is not an identity class also has the side-effect of making the class an identity class (8.1.1.5), with binary compatibility risks outlined in 13.4.1.

Deleting a method or constructor from a class may break compatibility with any pre-existing binary that referenced this method or constructor; a NoSuchMethodError may be thrown when such a reference from a pre-existing binary is linked. Such an error will occur only if no method with a matching signature and return type is declared in a superclass.

If the source code for a non-inner class contains no declared constructors, then a default constructor with no parameters is implicitly declared (8.8.9). Adding one or more constructor declarations to the source code of such a class will prevent this default constructor from being implicitly declared, effectively deleting a constructor, unless one of the new constructors also has no parameters, thus replacing the default constructor. The default constructor with no parameters is given the same access modifier as the class of its declaration, so any replacement should have as much or more access if compatibility with pre-existing binaries is to be preserved.

13.4.13 Method and Constructor Type Parameters

Adding or deleting a type parameter of a method or constructor does not, in itself, have any implications for binary compatibility.

If such a type parameter is used in the type of the method or constructor, that may have the normal implications of changing the aforementioned type.

Adding type parameters to the constructor of an abstract class that is not an identity class has the side-effect of making the class an identity class (8.1.1.5), with binary compatibility risks outlined in 13.4.1.

Renaming a type parameter of a method or constructor has no effect with respect to pre-existing binaries.

Changing the first bound of a type parameter of a method or constructor may change the erasure (4.6) of any member that uses that type parameter in its own type, and this may affect binary compatibility. Specifically:

Changing any other bound has no effect on binary compatibility.

13.4.20 synchronized Methods

Adding or deleting a synchronized modifier of to a method of an identity class does not break compatibility with pre-existing binaries.

Adding a synchronized modifier to a method of an abstract class that is not an identity class has the side-effect of making the class an identity class (8.1.1.5), with binary compatibility risks outlined in 13.4.1.

Deleting a synchronized modifier of a method does not break compatibility with pre-existing binaries.

13.4.21 Method and Constructor Throws

Changes to the throws clause of methods or constructors do not directly break compatibility with pre-existing binaries; these clauses are checked only at compile time.

However, adding a throws clause to the constructor of an abstract class that is not an identity class has the side-effect of making the class an identity class (8.1.1.5), with binary compatibility risks outlined in 13.4.1.

13.4.22 Method and Constructor Body

Changes to the body of a method or constructor do not directly break compatibility with pre-existing binaries.

However, changes to the body of the constructor of an abstract class that is not an identity class, other than adding or removing the super(); call, have the side-effect of making the class an identity class (8.1.1.5), with binary compatibility risks outlined in 13.4.1.

The keyword final on a method does not mean that the method can be safely inlined; it means only that the method cannot be overridden. It is still possible that a new version of that method will be provided at link-time. Furthermore, the structure of the original program must be preserved for purposes of reflection.

Therefore, we note that a Java compiler cannot expand a method inline at compile time. In general we suggest that implementations use late-bound (run-time) code generation and optimization.

13.5 Evolution of Interfaces

This section describes the impact of changes to the declaration of an interface and its members on pre-existing binaries.

13.5.9 identity and value Interfaces

This section might more appropriately appear as 13.5.3, with subsequent sections being renumbered.

If one of the identity or value modifiers is added to an interface, a pre-existing binary that attempts to extend or implement the interface may fail to load. Specifically, if the subclass or subinterface has an incompatible identity or value modifier, or extends a class or interface with an incompatible identity or value modifier, class loading will fail with an IncompatibleClassChangeError.

Removing one of the identity or value modifiers from an interface does not break compatibility with pre-existing binaries.

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 value class or interface type, or a type variable or intersection type bounded by a value class or interface 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 explicit constructor invocation statements (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).

Use of this in a constructor of a value class is restricted. It is a compile-time error if a this expression occurs in the constructor of a value class unless one of the following is true:

Let C by the innermost enclosing class or interface declaration of a this expression. 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.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, if the class is an identity class, 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 identity 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.

For an identity class, the constructor is provided with the newly-created class instance, and is responsible for fully initializing it. This results in includes invoking at least one constructor for each superclass of the class. This process can be directed by explicit constructor invocation statements (8.8.7.1) and is specified in detail in 12.5.

The When the identity class constructor completes, the value of a the class instance creation expression is a reference to the newly created object of the specified class. Every time the expression is evaluated, a fresh object is created.

For a value class, the constructor itself creates new variables for each of the class's instance fields, and defines a class instance by setting those variables. No superclass constructors are invoked.

When the value class constructor completes, the value of the class instance creation expression is a reference to a class instance with the determined field values. This instance may be the same as an instance created previously.

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 object or array (4.3.1); otherwise, the result is false.

The result of != is false if the operand values are both null or both refer to the same object or array; 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).

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.